irpack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/irpack +56 -0
- data/lib/irpack.rb +504 -0
- data/test/test_cscompiler.rb +48 -0
- data/test/test_packager.rb +56 -0
- metadata +70 -0
data/bin/irpack
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ir
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Copyright (c) 2010 Ryuichi Sakamoto.
|
5
|
+
|
6
|
+
This software is provided 'as-is', without any express or implied
|
7
|
+
warranty. In no event will the authors be held liable for any damages
|
8
|
+
arising from the use of this software.
|
9
|
+
|
10
|
+
Permission is granted to anyone to use this software for any purpose,
|
11
|
+
including commercial applications, and to alter it and redistribute it
|
12
|
+
freely, subject to the following restrictions:
|
13
|
+
|
14
|
+
1. The origin of this software must not be misrepresented; you must not
|
15
|
+
claim that you wrote the original software. If you use this software
|
16
|
+
in a product, an acknowledgment in the product documentation would be
|
17
|
+
appreciated but is not required.
|
18
|
+
|
19
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
20
|
+
misrepresented as being the original software.
|
21
|
+
|
22
|
+
3. This notice may not be removed or altered from any source
|
23
|
+
distribution.
|
24
|
+
=end
|
25
|
+
|
26
|
+
require 'irpack'
|
27
|
+
require 'optparse'
|
28
|
+
|
29
|
+
packdir = nil
|
30
|
+
output_file = nil
|
31
|
+
target = :exe
|
32
|
+
compress = false
|
33
|
+
stdlib = true
|
34
|
+
opt = OptionParser.new
|
35
|
+
opt.on('-d SOURCEDIR', 'Specify pack directory. [base of ENTRYFILE]') {|v| packdir = v }
|
36
|
+
opt.on('-o OUTPUTFILE', 'Specify output file name.') {|v| output_file = v }
|
37
|
+
opt.on('-w', 'Generate window app.') { target = :winexe }
|
38
|
+
opt.on('-W', 'Generate console app.[default]') { target = :exe }
|
39
|
+
opt.on('--compress', 'Compress package.') { compress = true }
|
40
|
+
opt.on('--no-stdlib', 'Do not include IronRuby assemblies.') {|v| stdlib = v }
|
41
|
+
opt.banner = "Usage: #{$0} [options] ENTRYFILE"
|
42
|
+
opt.parse!(ARGV)
|
43
|
+
|
44
|
+
if ARGV.size<1 then
|
45
|
+
$stderr.puts opt.help
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
entry_file = ARGV[0]
|
50
|
+
packdir ||= File.dirname(entry_file)
|
51
|
+
output_file ||= File.join(File.dirname(entry_file), File.basename(entry_file, '.*')+'.exe')
|
52
|
+
IRPack.pack_dir(packdir, entry_file, output_file,
|
53
|
+
:target => target,
|
54
|
+
:compress => compress,
|
55
|
+
:stdlib => stdlib)
|
56
|
+
|
data/lib/irpack.rb
ADDED
@@ -0,0 +1,504 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2010 Ryuichi Sakamoto.
|
3
|
+
|
4
|
+
This software is provided 'as-is', without any express or implied
|
5
|
+
warranty. In no event will the authors be held liable for any damages
|
6
|
+
arising from the use of this software.
|
7
|
+
|
8
|
+
Permission is granted to anyone to use this software for any purpose,
|
9
|
+
including commercial applications, and to alter it and redistribute it
|
10
|
+
freely, subject to the following restrictions:
|
11
|
+
|
12
|
+
1. The origin of this software must not be misrepresented; you must not
|
13
|
+
claim that you wrote the original software. If you use this software
|
14
|
+
in a product, an acknowledgment in the product documentation would be
|
15
|
+
appreciated but is not required.
|
16
|
+
|
17
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
18
|
+
misrepresented as being the original software.
|
19
|
+
|
20
|
+
3. This notice may not be removed or altered from any source
|
21
|
+
distribution.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'erb'
|
25
|
+
require 'fileutils'
|
26
|
+
require 'tmpdir'
|
27
|
+
require 'pathname'
|
28
|
+
require 'WindowsBase'
|
29
|
+
|
30
|
+
unless Dir.respond_to?(:mktmpdir) then
|
31
|
+
def Dir.mktmpdir(prefix_suffix=['d',''], tmpdir=Dir.tmpdir, &block)
|
32
|
+
if prefix_suffix.kind_of?(String) then
|
33
|
+
prefix = prefix_suffix
|
34
|
+
suffix = ''
|
35
|
+
else
|
36
|
+
prefix = prefix_suffix[0]
|
37
|
+
suffix = prefix_suffix[1]
|
38
|
+
end
|
39
|
+
n = 0
|
40
|
+
begin
|
41
|
+
path = File.join(tmpdir, "#{prefix}-#{Time.now.to_i}-#{$$}-#{rand(0x100000000).to_s(36)}-#{suffix}-#{n}")
|
42
|
+
Dir.mkdir(path, 0700)
|
43
|
+
rescue Errno::EEXIST
|
44
|
+
n += 1
|
45
|
+
retry
|
46
|
+
end
|
47
|
+
|
48
|
+
if block then
|
49
|
+
begin
|
50
|
+
block.call(path)
|
51
|
+
ensure
|
52
|
+
FileUtils.remove_entry_secure(path)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module IRPack
|
61
|
+
include System
|
62
|
+
include System::Reflection
|
63
|
+
|
64
|
+
module CSCompiler
|
65
|
+
include Microsoft::CSharp
|
66
|
+
include System::CodeDom::Compiler
|
67
|
+
include System::Reflection
|
68
|
+
|
69
|
+
module_function
|
70
|
+
def compiler_version
|
71
|
+
case System::Environment.version.major
|
72
|
+
when 4
|
73
|
+
'v4.0'
|
74
|
+
when 2
|
75
|
+
'v3.5'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def system_assemblies
|
80
|
+
case compiler_version
|
81
|
+
when 'v4.0'
|
82
|
+
[
|
83
|
+
'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
|
84
|
+
'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
|
85
|
+
'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
|
86
|
+
]
|
87
|
+
when 'v3.5'
|
88
|
+
[
|
89
|
+
'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
|
90
|
+
'WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
|
91
|
+
'System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
|
92
|
+
]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def system_assembly_files
|
97
|
+
system_assemblies.collect {|name|
|
98
|
+
Assembly.reflection_only_load(name.to_clr_string).location
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_assembly(paths, file)
|
103
|
+
path = paths.find {|path| File.exist?(File.join(path, file)) }
|
104
|
+
if path then
|
105
|
+
File.join(path, file)
|
106
|
+
else
|
107
|
+
file
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class CompileError < RuntimeError; end
|
112
|
+
def compile(target, srcs, references, resources, output_name)
|
113
|
+
opts = System::Collections::Generic::Dictionary[System::String,System::String].new
|
114
|
+
opts['CompilerVersion'] = compiler_version
|
115
|
+
@compiler = CSharpCodeProvider.new(opts)
|
116
|
+
srcs = srcs.kind_of?(Array) ? srcs : [srcs]
|
117
|
+
refs = system_assembly_files + references
|
118
|
+
refs = System::Array[System::String].new(refs.size) {|i| refs[i] }
|
119
|
+
icon = resources.find {|rc| File.extname(rc)=='.ico' }
|
120
|
+
|
121
|
+
params = CompilerParameters.new(refs, output_name, false)
|
122
|
+
params.generate_in_memory = false
|
123
|
+
compiler_options = ['/optimize+']
|
124
|
+
case target
|
125
|
+
when :exe, 'exe'
|
126
|
+
params.generate_executable = true
|
127
|
+
compiler_options << '/target:exe'
|
128
|
+
compiler_options << "/win32icon:#{icon}" if icon
|
129
|
+
when :winexe, 'winexe'
|
130
|
+
params.generate_executable = true
|
131
|
+
compiler_options << '/target:winexe'
|
132
|
+
compiler_options << "/win32icon:#{icon}" if icon
|
133
|
+
else
|
134
|
+
params.generate_executable = false
|
135
|
+
compiler_options << '/target:library'
|
136
|
+
end
|
137
|
+
params.compiler_options = compiler_options.join(' ')
|
138
|
+
resources.each do |rc|
|
139
|
+
params.embedded_resources.add(rc)
|
140
|
+
end
|
141
|
+
srcs = System::Array[System::String].new(srcs.size) {|i| srcs[i] }
|
142
|
+
result = @compiler.compile_assembly_from_source(params, srcs)
|
143
|
+
result.errors.each do |err|
|
144
|
+
if err.is_warning then
|
145
|
+
$stderr.puts(err.to_s)
|
146
|
+
else
|
147
|
+
raise CompileError, err.to_s
|
148
|
+
end
|
149
|
+
end
|
150
|
+
result.path_to_assembly
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
module IRPack
|
156
|
+
BootLoaderSource = <<CS
|
157
|
+
using System;
|
158
|
+
using System.IO;
|
159
|
+
using System.IO.Packaging;
|
160
|
+
using System.Reflection;
|
161
|
+
|
162
|
+
namespace <%= module_name %> {
|
163
|
+
public class BootLoader
|
164
|
+
{
|
165
|
+
private static Assembly LoadAssemblyFromPackage(AppDomain domain, Package package, string file)
|
166
|
+
{
|
167
|
+
var uri = PackUriHelper.CreatePartUri(new Uri(file, UriKind.Relative));
|
168
|
+
if (package.PartExists(uri)) {
|
169
|
+
var stream = package.GetPart(uri).GetStream(FileMode.Open, FileAccess.Read);
|
170
|
+
var raw = new byte[stream.Length];
|
171
|
+
stream.Read(raw, 0, (int)stream.Length);
|
172
|
+
stream.Close();
|
173
|
+
return domain.Load(raw);
|
174
|
+
}
|
175
|
+
else {
|
176
|
+
return null;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
private static Assembly LoadAssembly(AppDomain domain, Package package, string file)
|
181
|
+
{
|
182
|
+
try {
|
183
|
+
return domain.Load(file);
|
184
|
+
}
|
185
|
+
catch (FileNotFoundException)
|
186
|
+
{
|
187
|
+
return LoadAssemblyFromPackage(domain, package, file);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
public static int Main(string[] args)
|
192
|
+
{
|
193
|
+
AppDomain domain = AppDomain.CurrentDomain;
|
194
|
+
domain.AssemblyResolve += new ResolveEventHandler(delegate (object sender, ResolveEventArgs e) {
|
195
|
+
foreach (var asm in domain.GetAssemblies()) {
|
196
|
+
if (e.Name==asm.FullName) {
|
197
|
+
return asm;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
throw new FileNotFoundException(e.Name);
|
201
|
+
});
|
202
|
+
var stream = Assembly.GetEntryAssembly().GetManifestResourceStream(@"<%= package_file %>");
|
203
|
+
var package = Package.Open(stream, FileMode.Open, FileAccess.Read);
|
204
|
+
<% preload_assemblies.each do |asm| %>
|
205
|
+
LoadAssembly(domain, package, @"<%= asm %>");
|
206
|
+
<% end %>
|
207
|
+
var entry_point = LoadAssemblyFromPackage(domain, package, @"<%= module_name %>.EntryPoint.dll");
|
208
|
+
var main = entry_point.GetType(@"<%= module_name %>.EntryPoint").GetMethod("Main");
|
209
|
+
int result = (int)(main.Invoke(null, new object[] { package, args }));
|
210
|
+
package.Close();
|
211
|
+
return result;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
CS
|
216
|
+
|
217
|
+
EntryPointSource = <<CS
|
218
|
+
using System;
|
219
|
+
using System.IO;
|
220
|
+
using System.IO.Packaging;
|
221
|
+
using System.Reflection;
|
222
|
+
using Microsoft.Scripting;
|
223
|
+
using Microsoft.Scripting.Hosting;
|
224
|
+
|
225
|
+
namespace <%= module_name %> {
|
226
|
+
public class EntryPoint
|
227
|
+
{
|
228
|
+
public class PackagePAL : PlatformAdaptationLayer
|
229
|
+
{
|
230
|
+
public Package CurrentPackage { get; set; }
|
231
|
+
public PackagePAL(Package pkg)
|
232
|
+
{
|
233
|
+
CurrentPackage = pkg;
|
234
|
+
}
|
235
|
+
|
236
|
+
private Uri ToPackageLoadPath(string path)
|
237
|
+
{
|
238
|
+
var domain = AppDomain.CurrentDomain;
|
239
|
+
var fullpath = Path.GetFullPath(path);
|
240
|
+
var searchpath = Path.GetFullPath(
|
241
|
+
domain.RelativeSearchPath!=null ?
|
242
|
+
Path.Combine(domain.BaseDirectory, domain.RelativeSearchPath) :
|
243
|
+
domain.BaseDirectory);
|
244
|
+
if (fullpath.StartsWith(searchpath)) {
|
245
|
+
var relpath = fullpath.Substring(searchpath.Length, fullpath.Length-searchpath.Length);
|
246
|
+
return PackUriHelper.CreatePartUri(new Uri(relpath, UriKind.Relative));
|
247
|
+
}
|
248
|
+
else {
|
249
|
+
return PackUriHelper.CreatePartUri(new Uri(path, UriKind.Relative));
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
private Uri ToPackagePath(string path)
|
254
|
+
{
|
255
|
+
var fullpath = Path.GetFullPath(path);
|
256
|
+
var searchpath = Path.GetDirectoryName(Path.GetFullPath(Assembly.GetEntryAssembly().Location));
|
257
|
+
if (fullpath.StartsWith(searchpath)) {
|
258
|
+
var relpath = fullpath.Substring(searchpath.Length, fullpath.Length-searchpath.Length);
|
259
|
+
return PackUriHelper.CreatePartUri(new Uri(relpath, UriKind.Relative));
|
260
|
+
}
|
261
|
+
else {
|
262
|
+
return PackUriHelper.CreatePartUri(new Uri(path, UriKind.Relative));
|
263
|
+
}
|
264
|
+
}
|
265
|
+
|
266
|
+
public override Assembly LoadAssembly(string name)
|
267
|
+
{
|
268
|
+
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
|
269
|
+
if (asm.FullName==name) {
|
270
|
+
return asm;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
return Assembly.Load(name);
|
274
|
+
}
|
275
|
+
|
276
|
+
public override Assembly LoadAssemblyFromPath(string path)
|
277
|
+
{
|
278
|
+
try {
|
279
|
+
return Assembly.LoadFile(path);
|
280
|
+
}
|
281
|
+
catch (FileNotFoundException e) {
|
282
|
+
var uri = ToPackageLoadPath(path);
|
283
|
+
if (CurrentPackage.PartExists(uri)) {
|
284
|
+
var stream = CurrentPackage.GetPart(uri).GetStream(FileMode.Open, FileAccess.Read);
|
285
|
+
var raw = new byte[stream.Length];
|
286
|
+
stream.Read(raw, 0, (int)stream.Length);
|
287
|
+
stream.Close();
|
288
|
+
return Assembly.Load(raw);
|
289
|
+
}
|
290
|
+
else {
|
291
|
+
throw e;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
public override bool FileExists(string path)
|
297
|
+
{
|
298
|
+
if (File.Exists(path)) {
|
299
|
+
return true;
|
300
|
+
}
|
301
|
+
else {
|
302
|
+
var uri = ToPackagePath(path);
|
303
|
+
return CurrentPackage.PartExists(uri);
|
304
|
+
}
|
305
|
+
}
|
306
|
+
|
307
|
+
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share)
|
308
|
+
{
|
309
|
+
if (mode==FileMode.Open && access==FileAccess.Read) {
|
310
|
+
var uri = ToPackagePath(path);
|
311
|
+
if (CurrentPackage.PartExists(uri)) {
|
312
|
+
return CurrentPackage.GetPart(uri).GetStream(mode, access);
|
313
|
+
}
|
314
|
+
else {
|
315
|
+
return new FileStream(path, mode, access, share);
|
316
|
+
}
|
317
|
+
}
|
318
|
+
else {
|
319
|
+
return new FileStream(path, mode, access, share);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
|
324
|
+
{
|
325
|
+
if (mode==FileMode.Open && access==FileAccess.Read) {
|
326
|
+
var uri = ToPackagePath(path);
|
327
|
+
if (CurrentPackage.PartExists(uri)) {
|
328
|
+
return CurrentPackage.GetPart(uri).GetStream(mode, access);
|
329
|
+
}
|
330
|
+
else {
|
331
|
+
return new FileStream(path, mode, access, share, bufferSize);
|
332
|
+
}
|
333
|
+
}
|
334
|
+
else {
|
335
|
+
return new FileStream(path, mode, access, share, bufferSize);
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
public override Stream OpenInputFileStream(string path)
|
340
|
+
{
|
341
|
+
var uri = ToPackagePath(path);
|
342
|
+
if (CurrentPackage.PartExists(uri)) {
|
343
|
+
return CurrentPackage.GetPart(uri).GetStream(FileMode.Open, FileAccess.Read);
|
344
|
+
}
|
345
|
+
else {
|
346
|
+
return new FileStream(path, FileMode.Open, FileAccess.Read);
|
347
|
+
}
|
348
|
+
}
|
349
|
+
}
|
350
|
+
|
351
|
+
public class IRHost : ScriptHost
|
352
|
+
{
|
353
|
+
private PlatformAdaptationLayer PAL_;
|
354
|
+
public override PlatformAdaptationLayer PlatformAdaptationLayer { get { return PAL_; } }
|
355
|
+
public IRHost(Package pkg)
|
356
|
+
{
|
357
|
+
PAL_ = new PackagePAL(pkg);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
public static int Main(Package package, string[] args)
|
362
|
+
{
|
363
|
+
var runtime_setup = new ScriptRuntimeSetup();
|
364
|
+
runtime_setup.LanguageSetups.Add(IronRuby.Ruby.CreateRubySetup());
|
365
|
+
runtime_setup.Options["MainFile"] = "<%= entry_file %>";
|
366
|
+
runtime_setup.Options["Arguments"] = args;
|
367
|
+
runtime_setup.HostType = typeof(IRHost);
|
368
|
+
runtime_setup.HostArguments = new object[] { package };
|
369
|
+
var engine = IronRuby.Ruby.GetEngine(IronRuby.Ruby.CreateRuntime(runtime_setup));
|
370
|
+
try {
|
371
|
+
engine.ExecuteFile("<%= entry_file %>");
|
372
|
+
return 0;
|
373
|
+
}
|
374
|
+
catch (IronRuby.Builtins.SystemExit e) {
|
375
|
+
return e.Status;
|
376
|
+
}
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
CS
|
381
|
+
module_function
|
382
|
+
def bootloader_source(module_name, package_file, preload_assemblies)
|
383
|
+
ERB.new(BootLoaderSource).result(binding)
|
384
|
+
end
|
385
|
+
|
386
|
+
def entrypoint_source(module_name, entry_file)
|
387
|
+
ERB.new(EntryPointSource).result(binding)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
module IRPack
|
392
|
+
module Packager
|
393
|
+
include System
|
394
|
+
include System::IO::Packaging
|
395
|
+
|
396
|
+
RelType = 'http://schemas.openxmlformats.org/package/2006/relationships/meta data/core-properties'
|
397
|
+
module_function
|
398
|
+
def pack(files, package_file, compress=false)
|
399
|
+
compress_option = compress ? CompressionOption.normal : CompressionOption.not_compressed
|
400
|
+
package = Package.open(package_file, System::IO::FileMode.create)
|
401
|
+
files.each do |src, dest|
|
402
|
+
uri = PackUriHelper.create_part_uri(Uri.new(dest, UriKind.relative))
|
403
|
+
part = package.create_part(uri, 'application/octet-stream', compress_option)
|
404
|
+
stream = part.get_stream
|
405
|
+
File.open(src, 'rb') do |f|
|
406
|
+
data = f.read
|
407
|
+
stream.write(data, 0, data.size)
|
408
|
+
end
|
409
|
+
stream.close
|
410
|
+
package.create_relationship(uri, TargetMode.internal, RelType)
|
411
|
+
end
|
412
|
+
package.close
|
413
|
+
end
|
414
|
+
|
415
|
+
def pack_dir(dir_name, package_file)
|
416
|
+
files = {}
|
417
|
+
base = Pathname.new(dir_name)
|
418
|
+
Dir.glob(base.join('**', '*')) do |fn|
|
419
|
+
files[fn] = Pathname.new(fn).relative_path_from(base)
|
420
|
+
end
|
421
|
+
pack(files, package_file)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
AssemblyPath = ::File.dirname(Assembly.get_entry_assembly.location.to_s)
|
426
|
+
PreloadAssemblies = %w[
|
427
|
+
Microsoft.Dynamic.dll
|
428
|
+
Microsoft.Scripting.dll
|
429
|
+
Microsoft.Scripting.Core.dll
|
430
|
+
IronRuby.dll
|
431
|
+
IronRuby.Libraries.dll
|
432
|
+
IronRuby.Libraries.Yaml.dll
|
433
|
+
]
|
434
|
+
|
435
|
+
module_function
|
436
|
+
def path_to_module_name(filename)
|
437
|
+
name = File.basename(filename, '.*')
|
438
|
+
name.gsub!(/[^A-Za-z0-9_]/, '_')
|
439
|
+
/^[A-Za-z]/=~name ? name : ('M'+name)
|
440
|
+
end
|
441
|
+
|
442
|
+
def pack(files, entry_file, output_file, options={})
|
443
|
+
preload_assemblies = PreloadAssemblies.collect {|fn|
|
444
|
+
File.join(AssemblyPath, fn)
|
445
|
+
}.select {|fn|
|
446
|
+
File.exist?(fn)
|
447
|
+
}
|
448
|
+
basename = File.basename(output_file, '.*')
|
449
|
+
module_name = path_to_module_name(output_file)
|
450
|
+
resources = options[:resources] || []
|
451
|
+
target = options[:target] || :exe
|
452
|
+
stdlib = options.include?(:stdlib) ? options[:stdlib] : true
|
453
|
+
compress = options.include?(:compress) ? options[:compress] : false
|
454
|
+
pack_files = files.dup
|
455
|
+
|
456
|
+
Dir.mktmpdir(File.basename($0,'.*')) do |tmp_path|
|
457
|
+
entry_src = entrypoint_source(module_name, entry_file)
|
458
|
+
entry_dll = File.join(tmp_path, module_name+'.EntryPoint.dll')
|
459
|
+
CSCompiler.compile(:dll, entry_src, preload_assemblies, [], entry_dll)
|
460
|
+
pack_files[entry_dll] = File.basename(entry_dll)
|
461
|
+
if stdlib then
|
462
|
+
preload_assemblies.each do |asm|
|
463
|
+
pack_files[asm] = File.basename(asm)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
package_file = File.join(tmp_path, basename+'.pkg')
|
467
|
+
Packager.pack(pack_files, package_file, compress)
|
468
|
+
|
469
|
+
target_src = bootloader_source(
|
470
|
+
module_name,
|
471
|
+
File.basename(package_file),
|
472
|
+
preload_assemblies.collect {|fn| File.basename(fn) }
|
473
|
+
)
|
474
|
+
Dir.chdir(tmp_path) do
|
475
|
+
CSCompiler.compile(target, target_src, [], [File.basename(package_file)], output_file)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
def pack_dir(pack_dir, entry_file, output_file, options={})
|
481
|
+
output_file = File.expand_path(output_file)
|
482
|
+
entry_packed = nil
|
483
|
+
pack_files = {}
|
484
|
+
resources = []
|
485
|
+
base = Pathname.new(pack_dir).expand_path
|
486
|
+
Dir.glob(base.join('**', '*')) do |fn|
|
487
|
+
path = Pathname.new(File.expand_path(fn))
|
488
|
+
next if path==output_file
|
489
|
+
relpath = path.relative_path_from(base)
|
490
|
+
pack_files[path] = relpath
|
491
|
+
if not entry_packed and
|
492
|
+
(path.to_s==File.expand_path(entry_file) or
|
493
|
+
path.basename.to_s==entry_file or
|
494
|
+
relpath.to_s==entry_file) then
|
495
|
+
entry_packed = relpath
|
496
|
+
end
|
497
|
+
resources << path.to_s if path.extname=='.ico'
|
498
|
+
end
|
499
|
+
raise ArgumentError, "Entry file `#{entry_file}' is not in packed directory."unless entry_packed
|
500
|
+
options = options.merge(:resources => resources)
|
501
|
+
pack(pack_files, entry_packed, output_file, options)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'test/unit'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'irpack'
|
8
|
+
|
9
|
+
require 'WindowsBase'
|
10
|
+
include System
|
11
|
+
include System::IO
|
12
|
+
include System::IO::Packaging
|
13
|
+
|
14
|
+
class TC_CSCompiler < Test::Unit::TestCase
|
15
|
+
def test_compile_dll
|
16
|
+
src = <<-CS
|
17
|
+
using System;
|
18
|
+
namespace hoge {
|
19
|
+
class Hoge {
|
20
|
+
public static int Main(string[] args) {
|
21
|
+
System.Console.WriteLine("hoge");
|
22
|
+
return 0;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
CS
|
27
|
+
references = []
|
28
|
+
resources = []
|
29
|
+
output_file = Tempfile.new(File.basename(__FILE__))
|
30
|
+
output_file.close
|
31
|
+
result = IRPack::CSCompiler.compile(
|
32
|
+
:dll,
|
33
|
+
src,
|
34
|
+
references,
|
35
|
+
resources,
|
36
|
+
output_file.path
|
37
|
+
)
|
38
|
+
assert_equal(output_file.path, result)
|
39
|
+
asm = nil
|
40
|
+
assert_nothing_raised do
|
41
|
+
asm = System::Reflection::Assembly.load_from(output_file.path)
|
42
|
+
end
|
43
|
+
assert(asm.get_type('hoge.Hoge'))
|
44
|
+
assert_nil(asm.entry_point)
|
45
|
+
assert_equal(System::Reflection::Assembly.get_entry_assembly.image_runtime_version, asm.image_runtime_version)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'test/unit'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'irpack'
|
8
|
+
|
9
|
+
require 'WindowsBase'
|
10
|
+
include System
|
11
|
+
include System::IO
|
12
|
+
include System::IO::Packaging
|
13
|
+
|
14
|
+
class TC_Packager < Test::Unit::TestCase
|
15
|
+
SrcFiles = {
|
16
|
+
File.join(File.dirname(__FILE__), 'test_packager.rb') => 'foo.rb',
|
17
|
+
File.join(File.dirname(__FILE__), 'test_cscompiler.rb') => 'bar.rb',
|
18
|
+
}
|
19
|
+
|
20
|
+
def test_pack
|
21
|
+
target = Tempfile.new(File.basename(__FILE__))
|
22
|
+
target.close
|
23
|
+
IRPack::Packager.pack(SrcFiles, target.path)
|
24
|
+
package = Package.open(target.path, FileMode.open)
|
25
|
+
SrcFiles.each do |src, dest|
|
26
|
+
uri = Uri.new(File.join('/', dest), UriKind.relative)
|
27
|
+
assert(package.part_exists(uri))
|
28
|
+
stream = package.get_part(uri).get_stream
|
29
|
+
bytes = System::Array[System::Byte].new(stream.length)
|
30
|
+
stream.read(bytes, 0, stream.length)
|
31
|
+
assert_equal(File.open(src, 'rb'){|f|f.read}, bytes.to_a.pack('C*'))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_pack_dir
|
36
|
+
Dir.mktmpdir do |src_path|
|
37
|
+
SrcFiles.each do |src, dest|
|
38
|
+
FileUtils.mkdir_p(File.join(src_path, File.dirname(dest)))
|
39
|
+
FileUtils.copy(src, File.join(src_path, dest))
|
40
|
+
end
|
41
|
+
target = Tempfile.new(File.basename(__FILE__))
|
42
|
+
target.close
|
43
|
+
IRPack::Packager.pack_dir(src_path, target.path)
|
44
|
+
package = Package.open(target.path, FileMode.open)
|
45
|
+
SrcFiles.each do |src, dest|
|
46
|
+
uri = Uri.new(File.join('/', dest), UriKind.relative)
|
47
|
+
assert(package.part_exists(uri))
|
48
|
+
stream = package.get_part(uri).get_stream
|
49
|
+
bytes = System::Array[System::Byte].new(stream.length)
|
50
|
+
stream.read(bytes, 0, stream.length)
|
51
|
+
assert_equal(File.open(src, 'rb'){|f|f.read}, bytes.to_a.pack('C*'))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irpack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- kumaryu
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-09-11 00:00:00 +09:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: |
|
22
|
+
IRPack converts your IronRuby scripts to a standalone .exe file.
|
23
|
+
Generated executable does not require IronRuby, but only .NET Framework or mono.
|
24
|
+
|
25
|
+
email: kumaryu@kumayu.net
|
26
|
+
executables:
|
27
|
+
- irpack
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- bin/irpack
|
34
|
+
- lib/irpack.rb
|
35
|
+
- test/test_cscompiler.rb
|
36
|
+
- test/test_packager.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/kumaryu/irpack
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirements:
|
63
|
+
- none
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.7
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Generate a standalone executable file from IronRuby scripts.
|
69
|
+
test_files: []
|
70
|
+
|