ocra 1.2.0 → 1.3.0.rc1
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/History.txt +17 -0
- data/Manifest.txt +1 -1
- data/{README.txt → README.rdoc} +138 -63
- data/Rakefile +1 -4
- data/bin/ocra +492 -228
- data/lib/ocra.rb +1 -1
- data/share/ocra/edicon.exe +0 -0
- data/share/ocra/stub.exe +0 -0
- data/share/ocra/stubw.exe +0 -0
- data/test/test_ocra.rb +186 -65
- metadata +12 -14
data/History.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
=== 1.3.0.rc1
|
2
|
+
|
3
|
+
* Fixed some additional corner cases with absolute and relative
|
4
|
+
require & load paths. Extended test suite to cover a lot more
|
5
|
+
cases.
|
6
|
+
|
7
|
+
* Now provides a meaningful exit status code (1 on error, 0 on
|
8
|
+
success). (DavidMikeSimon)
|
9
|
+
|
10
|
+
* New option to _not_ run the script to detect dependencies
|
11
|
+
(--no-dep-run). (DavidMikeSimon)
|
12
|
+
|
13
|
+
* Bundler support using the --gemfile option. (DavidMikeSimon)
|
14
|
+
|
15
|
+
* Debug mode support in the stub (--debug). Also --debug-extract to
|
16
|
+
keep extracted files from executable. (DavidMikeSimon)
|
17
|
+
|
1
18
|
=== 1.2.0
|
2
19
|
|
3
20
|
* Ignore console events (Ctrl-C, Ctrl-Break). Ruby process handles
|
data/Manifest.txt
CHANGED
data/{README.txt → README.rdoc}
RENAMED
@@ -13,30 +13,62 @@ source code. The executable is a self-extracting, self-running
|
|
13
13
|
executable that contains the Ruby interpreter, your source code and
|
14
14
|
any additionally needed ruby libraries or DLL.
|
15
15
|
|
16
|
-
== FEATURES
|
16
|
+
== FEATURES
|
17
17
|
|
18
18
|
* LZMA Compression (optional, default on)
|
19
|
-
* Windows support only
|
20
19
|
* Ruby 1.9 support
|
21
20
|
* Both windowed/console mode supported
|
21
|
+
* Includes gems based on usage, or from a Bundler Gemfile
|
22
22
|
|
23
|
-
|
24
|
-
the issue tracker on GitHub
|
25
|
-
(http://github.com/larsch/ocra/issues). You can also join the Google
|
26
|
-
Group discussion forum to ask questions and get help
|
27
|
-
(http://groups.google.com/group/ruby-ocra).
|
23
|
+
== PROBLEMS & BUG REPORTING:
|
28
24
|
|
29
|
-
|
25
|
+
* Windows support only
|
30
26
|
|
31
|
-
|
27
|
+
If you experience problems with OCRA or have found a bug, please use
|
28
|
+
the issue tracker on GitHub (http://github.com/larsch/ocra/issues).
|
29
|
+
You can also join the Google Group discussion forum to ask questions
|
30
|
+
and get help (http://groups.google.com/group/ruby-ocra).
|
32
31
|
|
33
|
-
|
34
|
-
(gems and DLLs) into an executable named "script.exe".
|
32
|
+
== SYNOPSIS:
|
35
33
|
|
34
|
+
=== Building an executable:
|
35
|
+
|
36
|
+
ocra script.rb
|
37
|
+
|
38
|
+
Will package "<tt>script.rb</tt>", the Ruby interpreter and all
|
39
|
+
dependencies (gems and DLLs) into an executable named
|
40
|
+
"<tt>script.exe</tt>".
|
41
|
+
|
42
|
+
=== Command line:
|
43
|
+
|
44
|
+
ocra [options] script.rb [<other files> ...] [-- <script arguments> ...]
|
45
|
+
|
46
|
+
=== Options:
|
47
|
+
|
48
|
+
ocra --help
|
49
|
+
|
50
|
+
--dll dllname Include additional DLLs from the Ruby bindir.
|
51
|
+
--no-lzma Disable LZMA compression of the executable.
|
52
|
+
--no-dep-run Don't run script.rb to check for dependencies.
|
53
|
+
--add-all-core Add all core ruby libraries to the executable.
|
54
|
+
--output <file> Name the exe to generate. Defaults to ./<scriptname>.exe.
|
55
|
+
--gemfile <file> Add all gems and dependencies listed in a Bundler Gemfile.
|
56
|
+
--quiet Suppress output while building executable.
|
57
|
+
--verbose Show extra output while building executable.
|
58
|
+
--debug Executable will be verbose.
|
59
|
+
--debug-extract Executable will unpack to local dir and not delete after.
|
60
|
+
--help Display this information.
|
61
|
+
--windows Force Windows application (rubyw.exe)
|
62
|
+
--console Force console application (ruby.exe)
|
63
|
+
--no-autoload Don't load/include script.rb's autoloads.
|
64
|
+
--icon <ico> Replace icon with a custom one.
|
65
|
+
--version Display version number and exit.
|
66
|
+
--no-gem-filter Don't filter readme's, doc, C-source, etc. from gems.
|
67
|
+
|
36
68
|
=== Compilation:
|
37
69
|
|
38
|
-
* OCRA will load your script (using Kernel#load) and build
|
39
|
-
executable when it exits.
|
70
|
+
* OCRA will load your script (using <tt>Kernel#load</tt>) and build
|
71
|
+
the executable when it exits.
|
40
72
|
|
41
73
|
* Your program should 'require' all necessary files when invoked without
|
42
74
|
arguments, so OCRA can detect all dependencies.
|
@@ -46,8 +78,8 @@ Group discussion forum to ask questions and get help
|
|
46
78
|
|
47
79
|
* .rb files will become console applications. .rbw files will become
|
48
80
|
windowed application (without a console window popping
|
49
|
-
up). Alternatively, use the <tt>--console</tt> or
|
50
|
-
options.
|
81
|
+
up). Alternatively, use the "<tt>--console</tt>" or
|
82
|
+
"<tt>--windows</tt>" options.
|
51
83
|
|
52
84
|
=== Running your application:
|
53
85
|
|
@@ -83,20 +115,26 @@ Group discussion forum to ask questions and get help
|
|
83
115
|
* Windows
|
84
116
|
|
85
117
|
* Working Ruby installation, tested with:
|
86
|
-
|
87
|
-
|
118
|
+
* One-Click Installer (187_27_rc2)
|
119
|
+
* RubyInstaller (1.8.7p299, 1.9.1p249, 1.9.2p0)
|
88
120
|
|
89
121
|
* MinGW Installation (when working with the source code only)
|
90
122
|
|
91
123
|
== INSTALL:
|
92
124
|
|
93
|
-
=== Gem
|
125
|
+
=== Gem Package
|
126
|
+
|
127
|
+
Install by running:
|
94
128
|
|
95
129
|
gem install ocra
|
96
130
|
|
97
|
-
|
131
|
+
=== Alternate Gem download:
|
132
|
+
|
133
|
+
* http://rubyforge.org/frs/?group_id=8185
|
134
|
+
|
135
|
+
* http://rubygems.org/gems/ocra
|
98
136
|
|
99
|
-
=== Stand-alone
|
137
|
+
=== Stand-alone version
|
100
138
|
|
101
139
|
Get ocrasa.rb from http://rubyforge.org/frs/?group_id=8185. Requires
|
102
140
|
nothing but a working Ruby installation on Windows.
|
@@ -104,7 +142,8 @@ nothing but a working Ruby installation on Windows.
|
|
104
142
|
== TECHNICAL DETAILS
|
105
143
|
|
106
144
|
OCRA first runs the target script in order to detect any files that
|
107
|
-
are loaded and used at runtime (Using Kernel#require and
|
145
|
+
are loaded and used at runtime (Using <tt>Kernel#require</tt> and
|
146
|
+
<tt>Kernel#load</tt>).
|
108
147
|
|
109
148
|
OCRA embeds everything needed to run a Ruby script into a single
|
110
149
|
executable file. The file contains the .exe stub which is compiled
|
@@ -120,18 +159,19 @@ your application will be put in the 'src' subdirectory.
|
|
120
159
|
|
121
160
|
=== Libraries
|
122
161
|
|
123
|
-
Any code that is loaded through Kernel#require when your
|
124
|
-
executed will be included in the OCRA
|
125
|
-
code will not be loaded and included
|
126
|
-
is actually run when OCRA invokes
|
127
|
-
know about it and will not include
|
162
|
+
Any code that is loaded through <tt>Kernel#require</tt> when your
|
163
|
+
script is executed will be included in the OCRA
|
164
|
+
executable. Conditionally loaded code will not be loaded and included
|
165
|
+
in the executable unless the code is actually run when OCRA invokes
|
166
|
+
your script. Otherwise, OCRA won't know about it and will not include
|
167
|
+
the source files.
|
128
168
|
|
129
169
|
RubyGems are handled specially. Whenever a file from a Gem is
|
130
170
|
detected, OCRA will attempt to include all the files from that
|
131
171
|
specific Gem (all files listed in the manifest), expect some unlikely
|
132
|
-
needed files such as readme's and other documentation. This can
|
133
|
-
overruled using the
|
134
|
-
every file that is listed in the Gem's manifest.
|
172
|
+
needed files such as readme's and other documentation. This latter can
|
173
|
+
be overruled using the "<tt>--no-gem-filter</tt>" which will make OCRA
|
174
|
+
include every file that is listed in the Gem's manifest.
|
135
175
|
|
136
176
|
Libraries found in non-standard path (for example, if you invoke OCRA
|
137
177
|
with "ruby -I some/path") will be placed into the site dir
|
@@ -141,13 +181,45 @@ tree, since OCRA may place the files elsewhere when extracted into the
|
|
141
181
|
temporary directory.
|
142
182
|
|
143
183
|
In case your script (or any of its dependencies) sets up autoloaded
|
144
|
-
module using Kernel#autoload
|
145
|
-
to ensure that they are all included in the
|
146
|
-
doesn't exist will be ignored (a warning will
|
184
|
+
module using <tt>Kernel#autoload</tt>, OCRA will automatically try to
|
185
|
+
load them to ensure that they are all included in the
|
186
|
+
executable. Modules that doesn't exist will be ignored (a warning will
|
187
|
+
be logged).
|
147
188
|
|
148
189
|
Dynamic link libraries (.dll files, for example WxWidgets, or other
|
149
190
|
source files) will be detected and included by OCRA.
|
150
191
|
|
192
|
+
=== Including libraries non-automatically
|
193
|
+
|
194
|
+
If an application or framework is complicated enough that it tends
|
195
|
+
to confuse Ocra's automatic dependency resolution, then you can
|
196
|
+
use other means to specify what needs to be packaged with your app.
|
197
|
+
|
198
|
+
To disable automatic dependency resolution, use the --no-dep-run
|
199
|
+
option; with it, Ocra will skip executing your program during the
|
200
|
+
build process. You will also probably need to use the --add-all-core
|
201
|
+
option to include the Ruby core libraries.
|
202
|
+
|
203
|
+
If your app uses gems, then you can specify them in a
|
204
|
+
Bundler (http://gembundler.com/) Gemfile, then use the --gemfile
|
205
|
+
option to supply it to Ocra. Ocra will automatically include all
|
206
|
+
gems specified, and all their dependencies.
|
207
|
+
|
208
|
+
(Note: This assumes that the gems are installed in your system,
|
209
|
+
*not* locally packaged inside the app directory by "bundle package")
|
210
|
+
|
211
|
+
These options are particularly useful for packaging Rails
|
212
|
+
applications. For example, to package a Rails 3 app in the
|
213
|
+
directory "someapp" and create an exe named "someapp.exe", without
|
214
|
+
actually running the app during the build, you could use the
|
215
|
+
following command:
|
216
|
+
|
217
|
+
ocra someapp/script/server someapp --no-dep-run --add-all-core --gemfile someapp/Gemfile
|
218
|
+
|
219
|
+
Rails 2 apps can be packaged similarly, though you will have to
|
220
|
+
integrate them with Bundler (http://gembundler.com/rails23.html)
|
221
|
+
first.
|
222
|
+
|
151
223
|
=== Environment variables
|
152
224
|
|
153
225
|
OCRA executables clear the RUBYLIB environment variable before your
|
@@ -177,13 +249,13 @@ through the Windows Explorer), the users' current working directory
|
|
177
249
|
<tt>C:\\WINDOWS\\SYSTEM32</tt> when the executable is invoked through
|
178
250
|
a file association. You can optionally change the directory yourself:
|
179
251
|
|
180
|
-
|
252
|
+
Dir.chdir File.dirname($0)
|
181
253
|
|
182
254
|
If you wish to maintain the user's working directory, but need to
|
183
255
|
'require' additional Ruby scripts from the source directory, you can
|
184
256
|
add the following line to your script:
|
185
257
|
|
186
|
-
|
258
|
+
$LOAD_PATH.unshift File.dirname($0)
|
187
259
|
|
188
260
|
=== Load path mangling
|
189
261
|
|
@@ -193,7 +265,7 @@ directory being the same as where the script is located (See
|
|
193
265
|
above). If you have additional library files in directories below the
|
194
266
|
directory containing your source script you can use this idiom:
|
195
267
|
|
196
|
-
|
268
|
+
$LOAD_PATH.unshift File.join(File.dirname($0), 'path/to/script')
|
197
269
|
|
198
270
|
=== Detecting OCRA
|
199
271
|
|
@@ -202,10 +274,10 @@ looking for the 'Ocra' constant. If it is defined, OCRA is currenly
|
|
202
274
|
building the executable from your script. For example, you can use
|
203
275
|
this to avoid opening a GUI window when compiling executables:
|
204
276
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
277
|
+
app = MyApp.new
|
278
|
+
if not defined?(Ocra)
|
279
|
+
app.main_loop
|
280
|
+
end
|
209
281
|
|
210
282
|
=== Additional files and resources
|
211
283
|
|
@@ -213,32 +285,35 @@ You can add additional files to the OCRA executable (for example
|
|
213
285
|
images) by appending them to the command line. They should be placed
|
214
286
|
in the source directory with your main script (or a subdirectory).
|
215
287
|
|
216
|
-
|
288
|
+
ocra mainscript.rb someimage.jpeg docs/document.txt
|
217
289
|
|
218
290
|
This will create the following layout in the temporary directory when
|
219
291
|
your program is executed:
|
220
292
|
|
221
|
-
|
222
|
-
|
223
|
-
|
293
|
+
src/mainscript.rb
|
294
|
+
src/someimage.jpeg
|
295
|
+
src/docs/document.txt
|
224
296
|
|
225
|
-
|
226
|
-
|
297
|
+
Both files, directoriess and glob patterns can be specified on the
|
298
|
+
command line. Files will be added as-is. If a directory is specified,
|
299
|
+
OCRA will include all files found below that directory. Glob patterns
|
300
|
+
(See Dir.glob) can be used to specify a specific set of files, for
|
301
|
+
example:
|
227
302
|
|
228
|
-
|
303
|
+
ocra script.rb assets/**/*.png
|
229
304
|
|
230
305
|
=== Command Line Arguments
|
231
306
|
|
232
307
|
To pass command line argument to your script (both while building and
|
233
|
-
when run from the resulting executable), specify them after a
|
234
|
-
marker. For example:
|
308
|
+
when run from the resulting executable), specify them after a
|
309
|
+
"<tt>--</tt>" marker. For example:
|
235
310
|
|
236
|
-
|
311
|
+
ocra script.rb -- --some-options=value
|
237
312
|
|
238
|
-
This will pass "
|
239
|
-
when running the executable. Any extra argument specified by
|
240
|
-
when invoking the executable will be appended after the
|
241
|
-
arguments.
|
313
|
+
This will pass "<tt>--some-options=value</tt>" to the script when
|
314
|
+
build and when running the executable. Any extra argument specified by
|
315
|
+
the user when invoking the executable will be appended after the
|
316
|
+
compile-time arguments.
|
242
317
|
|
243
318
|
=== Window/Console
|
244
319
|
|
@@ -248,10 +323,10 @@ windowed applications (without console window) from .rbw-files.
|
|
248
323
|
Ruby on Windows provides two executables: ruby.exe is a console mode
|
249
324
|
application and rubyw.exe is a windowed application which does not
|
250
325
|
bring up a console window when launched using the Windows Explorer.
|
251
|
-
By default, or if the <tt>--console</tt> option is used, OCRA will use
|
326
|
+
By default, or if the "<tt>--console</tt>" option is used, OCRA will use
|
252
327
|
the console runtime (rubyw.exe). OCRA will automatically select the
|
253
328
|
windowed runtime when your script has the ".rbw" extension, or if you
|
254
|
-
specify the <tt>--windows</tt> command line option.
|
329
|
+
specify the "<tt>--windows</tt>" command line option.
|
255
330
|
|
256
331
|
If your application works in console mode but not in windowed mode,
|
257
332
|
first check if your script works without OCRA using rubyw.exe. A
|
@@ -262,14 +337,14 @@ buffers run full).
|
|
262
337
|
You can also try wrapping your script in an exception handler that
|
263
338
|
logs any errors to a file:
|
264
339
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
340
|
+
begin
|
341
|
+
# your script here
|
342
|
+
rescue Exception => e
|
343
|
+
File.open("except.log") do |f|
|
344
|
+
f.puts e.inspect
|
345
|
+
f.puts e.backtrace
|
346
|
+
end
|
347
|
+
end
|
273
348
|
|
274
349
|
== CREDITS:
|
275
350
|
|
data/Rakefile
CHANGED
@@ -8,6 +8,7 @@ Hoe.spec 'ocra' do |spec|
|
|
8
8
|
spec.email = "larsch@belunktum.dk"
|
9
9
|
spec.url = "http://ocra.rubyforge.org/"
|
10
10
|
spec.readme_file = 'README.rdoc'
|
11
|
+
spec.extra_rdoc_files = ['README.rdoc']
|
11
12
|
end
|
12
13
|
|
13
14
|
task :build_stub do
|
@@ -19,10 +20,6 @@ end
|
|
19
20
|
|
20
21
|
file 'share/ocra/stub.exe' => :build_stub
|
21
22
|
|
22
|
-
file 'README.txt' => 'README.rdoc' do
|
23
|
-
cp 'README.rdoc', 'README.txt'
|
24
|
-
end
|
25
|
-
|
26
23
|
task :test => :build_stub
|
27
24
|
|
28
25
|
task :standalone => [ 'bin/ocrasa.rb' ]
|
data/bin/ocra
CHANGED
@@ -2,47 +2,190 @@
|
|
2
2
|
# -*- ruby -*-
|
3
3
|
|
4
4
|
module Ocra
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
# Path handling class. Ruby's Pathname class is not used because it
|
6
|
+
# is case sensitive and doesn't handle paths with mixed path
|
7
|
+
# separators.
|
8
|
+
class Pathname
|
9
|
+
def Pathname.pwd
|
10
|
+
Pathname.new(Dir.pwd)
|
11
|
+
end
|
12
|
+
|
13
|
+
def Pathname.pathequal(a, b)
|
14
|
+
a.downcase == b.downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :path
|
18
|
+
SEPARATOR_PAT = /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/ # }
|
19
|
+
ABSOLUTE_PAT = /\A([A-Z]:)?#{SEPARATOR_PAT}/i
|
20
|
+
|
21
|
+
def initialize(path)
|
22
|
+
@path = path
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_native
|
26
|
+
@path.tr File::SEPARATOR, File::ALT_SEPARATOR
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_posix
|
30
|
+
@path.tr File::ALT_SEPARATOR, File::SEPARATOR
|
31
|
+
end
|
32
|
+
|
33
|
+
# Compute the relative path from the 'src' path (directory) to 'tgt'
|
34
|
+
# (directory or file). Return the absolute path to 'tgt' if it can't
|
35
|
+
# be reached from 'src'.
|
36
|
+
def relative_path_from(other)
|
37
|
+
a = @path.split(SEPARATOR_PAT)
|
38
|
+
b = other.path.split(SEPARATOR_PAT)
|
39
|
+
while a.first && b.first && Pathname.pathequal(a.first, b.first)
|
40
|
+
a.shift
|
41
|
+
b.shift
|
42
|
+
end
|
43
|
+
return other if Pathname.new(b.first).absolute?
|
44
|
+
b.size.times { a.unshift '..' }
|
45
|
+
return Pathname.new(a.join('/'))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Determines if 'src' is contained in 'tgt' (i.e. it is a subpath of
|
49
|
+
# 'tgt'). Both must be absolute paths and not contain '..'
|
50
|
+
def subpath?(other)
|
51
|
+
other = Ocra.Pathname(other)
|
52
|
+
src_normalized = to_posix.downcase
|
53
|
+
tgt_normalized = other.to_posix.downcase
|
54
|
+
src_normalized =~ /^#{Regexp.escape tgt_normalized}#{SEPARATOR_PAT}/i
|
55
|
+
end
|
13
56
|
|
14
|
-
|
57
|
+
def /(other)
|
58
|
+
Ocra.Pathname(@path + '/' + Ocra.Pathname(other).path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def append_to_filename!(s)
|
62
|
+
@path.sub!(/(\.[^.]*?|)$/) { s.to_s + $1 }
|
63
|
+
end
|
64
|
+
|
65
|
+
def ext(new_ext = nil)
|
66
|
+
if new_ext
|
67
|
+
Pathname.new(@path.sub(/(\.[^.]*?)?$/) { new_ext })
|
68
|
+
else
|
69
|
+
File.extname(@path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def ext?(expected_ext)
|
74
|
+
Pathname.pathequal(ext, expected_ext)
|
75
|
+
end
|
76
|
+
|
77
|
+
def ==(other); to_posix.downcase == other.to_posix.downcase; end
|
78
|
+
def =~(o); @path =~ o; end
|
79
|
+
def <=>(other); @path.casecmp(other.path); end
|
80
|
+
def exist?; File.exist?(@path); end
|
81
|
+
def file?; File.file?(@path); end
|
82
|
+
def directory?; File.directory?(@path); end
|
83
|
+
def absolute?; @path =~ ABSOLUTE_PAT; end
|
84
|
+
def dirname; Pathname.new(File.dirname(@path)); end
|
85
|
+
def basename; Pathname.new(File.basename(@path)); end
|
86
|
+
def expand(dir = nil); Pathname.new(File.expand_path(@path, dir && Ocra.Pathname(dir))); end
|
87
|
+
|
88
|
+
alias to_s to_posix
|
89
|
+
alias to_str to_posix
|
90
|
+
end
|
91
|
+
|
92
|
+
# Type conversion for the Pathname class. Works with Pathname,
|
93
|
+
# String, NilClass and arrays of any of these.
|
94
|
+
def self.Pathname(obj)
|
95
|
+
case obj
|
96
|
+
when Pathname
|
97
|
+
obj
|
98
|
+
when Array
|
99
|
+
obj.map { |x| Pathname(x) }
|
100
|
+
when String
|
101
|
+
Pathname.new(obj)
|
102
|
+
when NilClass
|
103
|
+
nil
|
104
|
+
else
|
105
|
+
raise ArgumentError, obj
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Variables describing the host's build environment.
|
110
|
+
module Host
|
111
|
+
class << self
|
112
|
+
def exec_prefix
|
113
|
+
@exec_prefix ||= Ocra.Pathname(RbConfig::CONFIG['exec_prefix'])
|
114
|
+
end
|
115
|
+
def sitelibdir
|
116
|
+
@sitelibdir ||= Ocra.Pathname(RbConfig::CONFIG['sitelibdir'])
|
117
|
+
end
|
118
|
+
def bindir
|
119
|
+
@bindir ||= Ocra.Pathname(RbConfig::CONFIG['bindir'])
|
120
|
+
end
|
121
|
+
def libruby_so
|
122
|
+
@libruby_so ||= Ocra.Pathname(RbConfig::CONFIG['LIBRUBY_SO'])
|
123
|
+
end
|
124
|
+
def exeext
|
125
|
+
RbConfig::CONFIG['EXEEXT'] || ".exe"
|
126
|
+
end
|
127
|
+
def rubyw_exe
|
128
|
+
@rubyw_exe ||= (RbConfig::CONFIG['rubyw_install_name'] || "rubyw") + exeext
|
129
|
+
end
|
130
|
+
def ruby_exe
|
131
|
+
@ruby_exe ||= (RbConfig::CONFIG['ruby_install_name'] || "ruby") + exeext
|
132
|
+
end
|
133
|
+
def tempdir
|
134
|
+
@tempdir ||= Ocra.Pathname(ENV['TEMP'])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Sorts and returns an array without duplicates. Works with complex
|
140
|
+
# objects (such as Pathname), in contrast to Array#uniq.
|
141
|
+
def self.sort_uniq(a)
|
142
|
+
a.sort.inject([]) { |r, e| r.last == e ? r : r << e }
|
143
|
+
end
|
144
|
+
|
145
|
+
VERSION = "1.3.0.rc1"
|
15
146
|
|
16
147
|
IGNORE_MODULES = /^enumerator.so$/
|
17
148
|
|
18
149
|
IGNORE_GEMFILES = %r{(
|
19
150
|
# Auxiliary files in the root of the gem
|
20
|
-
^(History|Install|Manifest|README|Licen[sc]e
|
151
|
+
^(\.\/)?(History|Install|Manifest|README|Licen[sc]e|Contributors|ChangeLog|BSD|GPL).*$ |
|
21
152
|
# Installation files in the root of the gem
|
22
|
-
^(Rakefile|setup.rb|extconf.rb)$ |
|
153
|
+
^(\.\/)?(Rakefile|setup.rb|extconf.rb)$ |
|
23
154
|
# Documentation/test directories in the root of the gem
|
24
|
-
^(doc|ext|examples|test|tests|benchmarks)\/ |
|
155
|
+
^(\.\/)?(doc|ext|examples|test|tests|benchmarks)\/ |
|
25
156
|
# Directories anywhere
|
26
|
-
(^|\/)(\.autotest|\.svn
|
157
|
+
(^|\/)(\.autotest|\.svn|\.cvs|\.git)(\/|$) |
|
27
158
|
# Unlikely extensions
|
28
159
|
\.(rdoc)$/
|
29
160
|
)}xi
|
30
161
|
|
31
|
-
|
32
|
-
|
33
|
-
|
162
|
+
# Alias for the temporary directory where files are extracted.
|
163
|
+
TEMPDIR_ROOT = Pathname.new("\xFF")
|
164
|
+
# Directory for source files in temporary directory.
|
165
|
+
SRCDIR = Pathname.new('src')
|
166
|
+
# Directory for Ruby binaries in temporary directory.
|
167
|
+
BINDIR = Pathname.new('bin')
|
168
|
+
# Directory for GEMHOME files in temporary directory.
|
169
|
+
GEMHOMEDIR = Pathname.new('gemhome')
|
34
170
|
|
35
171
|
@options = {
|
36
172
|
:lzma_mode => true,
|
37
173
|
:extra_dlls => [],
|
38
174
|
:files => [],
|
175
|
+
:run_script => true,
|
176
|
+
:add_all_core => false,
|
177
|
+
:output_override => nil,
|
39
178
|
:load_autoload => true,
|
40
179
|
:force_windows => false,
|
41
180
|
:force_console => false,
|
42
181
|
:icon_filename => nil,
|
182
|
+
:gemfile => nil,
|
43
183
|
:quiet => false,
|
184
|
+
:verbose => false,
|
44
185
|
:autodll => true,
|
45
186
|
:show_warnings => true,
|
187
|
+
:debug => false,
|
188
|
+
:debug_extract => false,
|
46
189
|
:gem_filter => true,
|
47
190
|
:arg => []
|
48
191
|
}
|
@@ -56,17 +199,26 @@ module Ocra
|
|
56
199
|
attr_reader :stubwimage
|
57
200
|
end
|
58
201
|
|
59
|
-
|
60
|
-
|
61
|
-
|
202
|
+
def Ocra.msg(s)
|
203
|
+
puts "=== #{s}" unless Ocra.quiet
|
204
|
+
end
|
205
|
+
|
206
|
+
def Ocra.verbose_msg(s)
|
207
|
+
puts s if Ocra.verbose and not Ocra.quiet
|
208
|
+
end
|
209
|
+
|
210
|
+
def Ocra.warn(s)
|
211
|
+
msg "WARNING: #{s}" if Ocra.show_warnings
|
62
212
|
end
|
63
213
|
|
64
|
-
def Ocra.
|
65
|
-
|
214
|
+
def Ocra.fatal_error(s)
|
215
|
+
puts "ERROR: #{s}"
|
216
|
+
exit 1
|
66
217
|
end
|
67
218
|
|
68
|
-
|
69
|
-
|
219
|
+
# Returns a binary blob store embedded in the current Ruby script.
|
220
|
+
def Ocra.get_next_embedded_image
|
221
|
+
DATA.read(DATA.readline.to_i).unpack("m")[0]
|
70
222
|
end
|
71
223
|
|
72
224
|
def Ocra.save_environment
|
@@ -86,20 +238,18 @@ module Ocra
|
|
86
238
|
@stubimage = get_next_embedded_image
|
87
239
|
@stubwimage = get_next_embedded_image
|
88
240
|
lzmaimage = get_next_embedded_image
|
89
|
-
@lzmapath =
|
241
|
+
@lzmapath = Host.tempdir / 'lzma.exe'
|
90
242
|
File.open(@lzmapath, "wb") { |file| file << lzmaimage }
|
91
243
|
ediconimage = get_next_embedded_image
|
92
|
-
@ediconpath =
|
244
|
+
@ediconpath = Host.tempdir / 'edicon.exe'
|
93
245
|
File.open(@ediconpath, "wb") { |file| file << ediconimage }
|
94
246
|
else
|
95
|
-
ocrapath = File.dirname(__FILE__)
|
96
|
-
@stubimage = File.open(
|
97
|
-
@stubwimage = File.open(
|
98
|
-
@lzmapath =
|
99
|
-
@ediconpath =
|
100
|
-
end
|
101
|
-
@lzmapath = Ocra.dospath(@lzmapath)
|
102
|
-
@ediconpath = Ocra.dospath(@ediconpath)
|
247
|
+
ocrapath = Pathname(File.dirname(__FILE__))
|
248
|
+
@stubimage = File.open(ocrapath / '../share/ocra/stub.exe', "rb") { |file| file.read }
|
249
|
+
@stubwimage = File.open(ocrapath / '../share/ocra/stubw.exe', "rb") { |file| file.read }
|
250
|
+
@lzmapath = (ocrapath / '../share/ocra/lzma.exe').expand
|
251
|
+
@ediconpath = (ocrapath / '../share/ocra/edicon.exe').expand
|
252
|
+
end
|
103
253
|
end
|
104
254
|
|
105
255
|
def Ocra.parseargs(argv)
|
@@ -108,24 +258,39 @@ ocra [options] script.rb
|
|
108
258
|
|
109
259
|
--dll dllname Include additional DLLs from the Ruby bindir.
|
110
260
|
--no-lzma Disable LZMA compression of the executable.
|
111
|
-
--
|
261
|
+
--no-dep-run Don't run script.rb to check for dependencies.
|
262
|
+
--add-all-core Add all core ruby libraries to the executable.
|
263
|
+
--output <file> Name the exe to generate. Defaults to ./<scriptname>.exe.
|
264
|
+
--gemfile <file> Add all gems and dependencies listed in a Bundler Gemfile.
|
265
|
+
--quiet Suppress output while building executable.
|
266
|
+
--verbose Show extra output while building executable.
|
267
|
+
--debug Executable will be verbose.
|
268
|
+
--debug-extract Executable will unpack to local dir and not delete after.
|
112
269
|
--help Display this information.
|
113
270
|
--windows Force Windows application (rubyw.exe)
|
114
271
|
--console Force console application (ruby.exe)
|
115
|
-
--no-autoload Don't load/include script.rb's autoloads
|
116
|
-
--icon <ico> Replace icon with a custom one
|
117
|
-
--version Display version number
|
118
|
-
--no-gem-filter Don't filter readme's, doc, C-source, etc. from gems
|
272
|
+
--no-autoload Don't load/include script.rb's autoloads.
|
273
|
+
--icon <ico> Replace icon with a custom one.
|
274
|
+
--version Display version number and exit.
|
275
|
+
--no-gem-filter Don't filter readme's, doc, C-source, etc. from gems.
|
119
276
|
EOF
|
120
277
|
|
121
278
|
while arg = argv.shift
|
122
279
|
case arg
|
123
280
|
when /\A--(no-)?lzma\z/
|
124
281
|
@options[:lzma_mode] = !$1
|
282
|
+
when /\A--no-dep-run\z/
|
283
|
+
@options[:run_script] = false
|
284
|
+
when /\A--add-all-core\z/
|
285
|
+
@options[:add_all_core] = true
|
286
|
+
when /\A--output\z/
|
287
|
+
@options[:output_override] = Pathname(argv.shift)
|
125
288
|
when /\A--dll\z/
|
126
289
|
@options[:extra_dlls] << argv.shift
|
127
290
|
when /\A--quiet\z/
|
128
291
|
@options[:quiet] = true
|
292
|
+
when /\A--verbose\z/
|
293
|
+
@options[:verbose] = true
|
129
294
|
when /\A--windows\z/
|
130
295
|
@options[:force_windows] = true
|
131
296
|
when /\A--console\z/
|
@@ -133,15 +298,22 @@ EOF
|
|
133
298
|
when /\A--no-autoload\z/
|
134
299
|
@options[:load_autoload] = false
|
135
300
|
when /\A--icon\z/
|
136
|
-
@options[:icon_filename] = argv.shift
|
137
|
-
|
301
|
+
@options[:icon_filename] = Pathname(argv.shift)
|
302
|
+
Ocra.fatal_error "Icon file #{icon_filename} not found.\n" unless icon_filename.exist?
|
303
|
+
when /\A--gemfile\z/
|
304
|
+
@options[:gemfile] = Pathname(argv.shift)
|
305
|
+
Ocra.fatal_error "Gemfile #{gemfile} not found.\n" unless gemfile.exist?
|
138
306
|
when /\A--no-autodll\z/
|
139
307
|
@options[:autodll] = false
|
140
308
|
when /\A--version\z/
|
141
309
|
puts "Ocra #{VERSION}"
|
142
|
-
exit
|
310
|
+
exit 0
|
143
311
|
when /\A--no-warnings\z/
|
144
312
|
@options[:show_warnings] = false
|
313
|
+
when /\A--debug\z/
|
314
|
+
@options[:debug] = true
|
315
|
+
when /\A--debug-extract\z/
|
316
|
+
@options[:debug_extract] = true
|
145
317
|
when /\A--no-gem-filter\z/
|
146
318
|
@options[:gem_filter] = false
|
147
319
|
when /\A--\z/
|
@@ -149,7 +321,7 @@ EOF
|
|
149
321
|
ARGV.clear
|
150
322
|
when /\A--help\z/, /\A--./
|
151
323
|
puts usage
|
152
|
-
exit
|
324
|
+
exit 0
|
153
325
|
else
|
154
326
|
@options[:files] << arg
|
155
327
|
end
|
@@ -157,8 +329,19 @@ EOF
|
|
157
329
|
|
158
330
|
if files.empty?
|
159
331
|
puts usage
|
160
|
-
exit
|
332
|
+
exit 1
|
161
333
|
end
|
334
|
+
|
335
|
+
@options[:files].map! { |path|
|
336
|
+
path = path.tr('\\','/')
|
337
|
+
if File.directory?(path)
|
338
|
+
# If a directory is passed, we want all files under that directory
|
339
|
+
path = "#{path}/**/*"
|
340
|
+
end
|
341
|
+
files = Dir[path]
|
342
|
+
Ocra.fatal_error "#{path} not found!" if files.empty?
|
343
|
+
files.map { |path| Pathname(path).expand }
|
344
|
+
}.flatten!
|
162
345
|
end
|
163
346
|
|
164
347
|
def Ocra.init(argv)
|
@@ -171,7 +354,7 @@ EOF
|
|
171
354
|
# (and hence classes), and checks their constants for autoloaded
|
172
355
|
# ones, then attempts to load them.
|
173
356
|
def Ocra.attempt_load_autoload
|
174
|
-
modules_checked =
|
357
|
+
modules_checked = {}
|
175
358
|
loop do
|
176
359
|
modules_to_check = []
|
177
360
|
ObjectSpace.each_object(Module) do |mod|
|
@@ -179,15 +362,15 @@ EOF
|
|
179
362
|
end
|
180
363
|
break if modules_to_check.empty?
|
181
364
|
modules_to_check.each do |mod|
|
182
|
-
modules_checked
|
365
|
+
modules_checked[mod] = true
|
183
366
|
mod.constants.each do |const|
|
184
367
|
if mod.autoload?(const)
|
185
368
|
begin
|
186
369
|
mod.const_get(const)
|
187
370
|
rescue NameError
|
188
|
-
|
371
|
+
Ocra.warn "#{mod}::#{const} was defined autoloadable, but caused NameError"
|
189
372
|
rescue LoadError
|
190
|
-
|
373
|
+
Ocra.warn "#{mod}::#{const} was not loadable"
|
191
374
|
end
|
192
375
|
end
|
193
376
|
end
|
@@ -195,65 +378,43 @@ EOF
|
|
195
378
|
end
|
196
379
|
end
|
197
380
|
|
198
|
-
# Compute the relative path from the 'src' path (directory) to 'tgt'
|
199
|
-
# (directory or file). Return the absolute path to 'tgt' if it can't
|
200
|
-
# be reached from 'src'.
|
201
|
-
def Ocra.relative_path(src, tgt)
|
202
|
-
a = posixpath(src).split('/')
|
203
|
-
b = posixpath(tgt).split('/')
|
204
|
-
while a.first && a.first.downcase == b.first.downcase
|
205
|
-
a.shift
|
206
|
-
b.shift
|
207
|
-
end
|
208
|
-
return tgt if abspath?(b.first)
|
209
|
-
a.size.times { b.unshift '..' }
|
210
|
-
return b.join('/')
|
211
|
-
end
|
212
|
-
|
213
|
-
# Determines if 'src' is contained in 'tgt' (i.e. it is a subpath of
|
214
|
-
# 'tgt'). Both must be absolute paths and not contain '..'
|
215
|
-
def Ocra.subpath?(src, tgt)
|
216
|
-
src_normalized = Ocra.dospath(src).downcase
|
217
|
-
tgt_normalized = Ocra.dospath(tgt).downcase
|
218
|
-
src_normalized =~ /^#{Regexp.escape tgt_normalized}[\/\\]/i
|
219
|
-
end
|
220
|
-
|
221
|
-
# Returns true if 'path' is absolute, false otherwise.
|
222
|
-
def Ocra.abspath?(path)
|
223
|
-
path =~ /\A[A-Z]:/i
|
224
|
-
end
|
225
|
-
|
226
381
|
# Guess the load path (from 'paths') that was used to load
|
227
382
|
# 'path'. This is primarily relevant on Ruby 1.8 which stores
|
228
383
|
# "unqualified" paths in $LOADED_FEATURES.
|
229
|
-
def Ocra.find_load_path(
|
230
|
-
if
|
231
|
-
#
|
232
|
-
|
233
|
-
|
384
|
+
def Ocra.find_load_path(loadpaths, feature)
|
385
|
+
if feature.absolute?
|
386
|
+
# Choose those loadpaths which contain the feature
|
387
|
+
candidate_loadpaths = loadpaths.select { |loadpath| feature.subpath?(loadpath.expand) }
|
388
|
+
# Guess the require'd feature
|
389
|
+
feature_pairs = candidate_loadpaths.map { |loadpath| [loadpath, feature.relative_path_from(loadpath.expand)] }
|
390
|
+
# Select the shortest possible require-path (longest load-path)
|
391
|
+
if feature_pairs.empty?
|
392
|
+
nil
|
393
|
+
else
|
394
|
+
feature_pairs.sort_by { |loadpath, feature| feature.path.size }.first[0]
|
395
|
+
end
|
234
396
|
else
|
235
|
-
# Select the
|
236
|
-
candidates =
|
237
|
-
candidates.sort_by {|
|
397
|
+
# Select the loadpaths that contain 'feature' and select the shortest
|
398
|
+
candidates = loadpaths.select { |loadpath| feature.expand(loadpath).exist? }
|
399
|
+
candidates.sort_by { |loadpath| loadpath.path.size }.last
|
238
400
|
end
|
239
401
|
end
|
240
|
-
|
402
|
+
|
241
403
|
# Find the root of all files specified on the command line and use
|
242
404
|
# it as the "src" of the output.
|
243
405
|
def Ocra.find_src_root(files)
|
244
|
-
src_files = files.map { |file|
|
245
|
-
|
246
|
-
|
247
|
-
if subpath?(path, @ruby_exec_prefix)
|
406
|
+
src_files = files.map { |file| file.expand }
|
407
|
+
src_prefix = src_files.inject(src_files.first.dirname) do |srcroot, path|
|
408
|
+
if path.subpath?(Host.exec_prefix)
|
248
409
|
srcroot
|
249
410
|
else
|
250
411
|
loop do
|
251
|
-
relpath =
|
252
|
-
if
|
253
|
-
|
412
|
+
relpath = path.relative_path_from(srcroot)
|
413
|
+
if relpath.absolute?
|
414
|
+
Ocra.fatal_error "No common directory contains all specified files"
|
254
415
|
end
|
255
|
-
if relpath =~ /^\.\.\//
|
256
|
-
srcroot =
|
416
|
+
if relpath.to_s =~ /^\.\.\//
|
417
|
+
srcroot = srcroot.dirname
|
257
418
|
else
|
258
419
|
break
|
259
420
|
end
|
@@ -261,7 +422,7 @@ EOF
|
|
261
422
|
srcroot
|
262
423
|
end
|
263
424
|
end
|
264
|
-
src_files = src_files.map { |file|
|
425
|
+
src_files = src_files.map { |file| file.relative_path_from(src_prefix) }
|
265
426
|
return src_prefix, src_files
|
266
427
|
end
|
267
428
|
|
@@ -273,43 +434,65 @@ EOF
|
|
273
434
|
# file from a gem path.
|
274
435
|
def Ocra.find_gem_files(features)
|
275
436
|
features_from_gems = []
|
437
|
+
gems = []
|
438
|
+
|
439
|
+
# If a Bundler Gemfile was provided, add all gems it specifies
|
440
|
+
if Ocra.gemfile
|
441
|
+
Ocra.msg "Scanning Gemfile"
|
442
|
+
# Load Rubygems and Bundler so we can scan the Gemfile
|
443
|
+
['rubygems', 'bundler'].each do |lib|
|
444
|
+
begin
|
445
|
+
require lib
|
446
|
+
rescue LoadError
|
447
|
+
Ocra.fatal_error "Couldn't scan Gemfile, unable to load #{lib}"
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
ENV['BUNDLE_GEMFILE'] = Ocra.gemfile
|
452
|
+
Bundler.load.specs.each do |spec|
|
453
|
+
gems << [Pathname(spec.installation_path), spec.full_name]
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
276
457
|
if defined?(Gem)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
next if filename.nil? # Could be enumerator.so
|
458
|
+
features.each do |feature|
|
459
|
+
if not feature.absolute?
|
460
|
+
feature = find_load_path(Pathname($:), feature)
|
461
|
+
next if feature.nil? # Could be enumerator.so
|
282
462
|
end
|
283
|
-
Gem.path
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
463
|
+
gempaths = Pathname(Gem.path)
|
464
|
+
gempaths.each do |gempath|
|
465
|
+
geminstallpath = Pathname(gempath) / "gems"
|
466
|
+
if feature.subpath?(geminstallpath)
|
467
|
+
gemlocalpath = feature.relative_path_from(geminstallpath)
|
468
|
+
fullgemname = gemlocalpath.path.split('/').first
|
288
469
|
gems << [gempath, fullgemname]
|
289
|
-
features_from_gems <<
|
470
|
+
features_from_gems << feature
|
290
471
|
end
|
291
472
|
end
|
292
473
|
end
|
293
|
-
|
474
|
+
|
475
|
+
gems = sort_uniq(gems)
|
294
476
|
gem_files = []
|
295
477
|
gems.each do |gempath, fullgemname|
|
296
|
-
gemspecpath =
|
478
|
+
gemspecpath = gempath / 'specifications' / "#{fullgemname}.gemspec"
|
297
479
|
@gemspecs << gemspecpath
|
298
480
|
spec = Gem::Specification.load(gemspecpath)
|
481
|
+
Ocra.msg "Will include gem #{spec.full_name}"
|
299
482
|
|
300
483
|
# Get list of files
|
301
|
-
files = spec.files
|
484
|
+
files = Pathname(spec.files)
|
302
485
|
# Filter out some unlikely files (Readme, etc.)
|
303
486
|
files = files.select { |filename| filename !~ IGNORE_GEMFILES } if Ocra.gem_filter
|
304
487
|
# Find the full path
|
305
|
-
files = files.map { |file|
|
488
|
+
files = files.map { |file| (gempath / "gems" / fullgemname / file).expand }
|
306
489
|
# Filter out non-files
|
307
|
-
files = files.select { |file|
|
490
|
+
files = files.select { |file| file.file? }
|
308
491
|
|
309
492
|
gem_files += files
|
310
493
|
end
|
311
|
-
gem_files
|
312
|
-
features -= features_from_gems
|
494
|
+
gem_files = sort_uniq(gem_files)
|
495
|
+
features -= features_from_gems # FIXME Isn't this duplicated by caller?
|
313
496
|
else
|
314
497
|
gem_files = []
|
315
498
|
end
|
@@ -317,112 +500,158 @@ EOF
|
|
317
500
|
end
|
318
501
|
|
319
502
|
def Ocra.build_exe
|
320
|
-
|
503
|
+
all_load_paths = $LOAD_PATH.map { |loadpath| Pathname(loadpath).expand }
|
504
|
+
@added_load_paths = ($LOAD_PATH - @load_path_before).map { |loadpath| Pathname(loadpath).expand }
|
505
|
+
working_directory = Pathname.pwd.expand
|
321
506
|
|
322
507
|
restore_environment
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
508
|
+
|
509
|
+
features = []
|
510
|
+
# If the script was ran, then detect the features it used
|
511
|
+
if Ocra.run_script
|
512
|
+
# Attempt to autoload libraries before doing anything else.
|
513
|
+
attempt_load_autoload if Ocra.load_autoload
|
514
|
+
|
515
|
+
# Store the currently loaded files (before we require rbconfig for
|
516
|
+
# our own use).
|
517
|
+
features = $LOADED_FEATURES.map { |feature| Pathname(feature) }
|
518
|
+
features.delete_if { |feature| feature =~ IGNORE_MODULES }
|
519
|
+
end
|
330
520
|
|
331
521
|
# Find gemspecs to include
|
332
522
|
if defined?(Gem)
|
333
|
-
@gemspecs = Gem.loaded_specs.map { |name,info| info.loaded_from }
|
523
|
+
@gemspecs = Gem.loaded_specs.map { |name,info| Pathname(info.loaded_from) }
|
334
524
|
else
|
335
525
|
@gemspecs = []
|
336
526
|
end
|
337
527
|
|
338
528
|
require 'rbconfig'
|
339
|
-
|
340
|
-
src_prefix = Dir.pwd
|
341
|
-
sitelibdir = RbConfig::CONFIG['sitelibdir']
|
342
|
-
bindir = RbConfig::CONFIG['bindir']
|
343
|
-
libruby_so = RbConfig::CONFIG['LIBRUBY_SO']
|
344
|
-
instsitelibdir = relative_path(@ruby_exec_prefix, sitelibdir)
|
529
|
+
instsitelibdir = Host.sitelibdir.relative_path_from(Host.exec_prefix)
|
345
530
|
|
346
531
|
load_path = []
|
347
|
-
|
348
|
-
# Find the source root and adjust paths
|
349
|
-
src_prefix, src_files = find_src_root(Ocra.files)
|
350
|
-
Ocra.files.replace(src_files)
|
532
|
+
src_load_path = []
|
351
533
|
|
352
534
|
# Find gems files and remove them from features
|
353
535
|
gem_files, features_from_gems = find_gem_files(features)
|
354
536
|
features -= features_from_gems
|
355
537
|
|
538
|
+
# Find the source root and adjust paths
|
539
|
+
src_prefix, src_files = find_src_root(Ocra.files)
|
540
|
+
|
356
541
|
# Find features and decide where to put them in the temporary
|
357
542
|
# directory layout.
|
358
543
|
libs = []
|
359
|
-
features.each do |
|
360
|
-
path = find_load_path(
|
361
|
-
if path
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
puts "=== WARNING: Detected a relative require (#{filename}). This is not recommended." if Ocra.show_warnings
|
544
|
+
features.each do |feature|
|
545
|
+
path = find_load_path(all_load_paths, feature)
|
546
|
+
if path.nil? || path.expand == Pathname.pwd
|
547
|
+
Ocra.files << feature
|
548
|
+
else
|
549
|
+
if feature.absolute?
|
550
|
+
feature = feature.relative_path_from(path.expand)
|
367
551
|
end
|
368
|
-
fullpath =
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
libs << [ fullpath,
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
targetpath = File.join("gemhome", relative_path(gemhome, fullpath))
|
552
|
+
fullpath = feature.expand(path)
|
553
|
+
|
554
|
+
if fullpath.subpath?(Host.exec_prefix)
|
555
|
+
# Features found in the Ruby installation are put in the
|
556
|
+
# temporary Ruby installation.
|
557
|
+
libs << [ fullpath, fullpath.relative_path_from(Host.exec_prefix) ]
|
558
|
+
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| fullpath.subpath?(pth) }
|
559
|
+
# Features found in any other Gem path (e.g. ~/.gems) is put
|
560
|
+
# in a special 'gemhome' folder.
|
561
|
+
targetpath = GEMHOMEDIR / fullpath.relative_path_from(gemhome)
|
379
562
|
libs << [ fullpath, targetpath ]
|
563
|
+
elsif fullpath.subpath?(src_prefix) || path == working_directory
|
564
|
+
# Any feature found inside the src_prefix automatically gets
|
565
|
+
# added as a source file (to go in 'src').
|
566
|
+
Ocra.files << fullpath
|
567
|
+
# Add the load path unless it was added by the script while
|
568
|
+
# running (or we assume that the script can also set it up
|
569
|
+
# correctly when running from the resulting executable).
|
570
|
+
src_load_path << path unless @added_load_paths.include?(path)
|
571
|
+
elsif @added_load_paths.include?(path)
|
572
|
+
# Any feature that exist in a load path added by the script
|
573
|
+
# itself is added as a file to go into the 'src' (src_prefix
|
574
|
+
# will be adjusted below to point to the common parent).
|
575
|
+
Ocra.files << fullpath
|
380
576
|
else
|
381
|
-
|
577
|
+
# All other feature that can not be resolved go in the the
|
578
|
+
# Ruby sitelibdir. This is automatically in the load path
|
579
|
+
# when Ruby starts.
|
580
|
+
libs << [ fullpath, instsitelibdir / feature ]
|
382
581
|
end
|
383
|
-
else
|
384
|
-
puts "=== WARNING: Couldn't find #{filename}" unless filename =~ IGNORE_MODULES if Ocra.show_warnings
|
385
582
|
end
|
386
583
|
end
|
387
584
|
|
585
|
+
# Recompute the src_prefix. Files may have been added implicitly
|
586
|
+
# while scanning through features.
|
587
|
+
src_prefix, src_files = find_src_root(Ocra.files)
|
588
|
+
Ocra.files.replace(src_files)
|
589
|
+
|
590
|
+
# Add the load path that are required with the correct path after
|
591
|
+
# src_prefix was adjusted.
|
592
|
+
load_path += src_load_path.map { |loadpath| TEMPDIR_ROOT / SRCDIR / loadpath.relative_path_from(src_prefix) }
|
593
|
+
|
388
594
|
# Decide where to put gem files, either the system gem folder, or
|
389
595
|
# GEMHOME.
|
390
596
|
gem_files.each do |gemfile|
|
391
|
-
if subpath?(
|
392
|
-
libs << [ gemfile,
|
393
|
-
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| subpath?(
|
394
|
-
targetpath =
|
597
|
+
if gemfile.subpath?(Host.exec_prefix)
|
598
|
+
libs << [ gemfile, gemfile.relative_path_from(Host.exec_prefix) ]
|
599
|
+
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| gemfile.subpath?(pth) }
|
600
|
+
targetpath = GEMHOMEDIR / fullpath.relative_path_from(gemhome)
|
395
601
|
libs << [ gemfile, targetpath ]
|
396
602
|
else
|
397
|
-
|
603
|
+
Ocra.fatal_error "Don't know where to put gemfile #{gemfile}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
# If requested, add all ruby standard libraries
|
608
|
+
if Ocra.add_all_core
|
609
|
+
Ocra.msg "Will include all ruby core libraries"
|
610
|
+
@load_path_before.each do |lp|
|
611
|
+
path = Pathname.new(lp)
|
612
|
+
next unless path.to_posix =~
|
613
|
+
/\/(ruby\/(?:site_ruby\/|vendor_ruby\/)?[0-9.]+)\/?$/i
|
614
|
+
subdir = $1
|
615
|
+
Dir["#{lp}/**/*"].each do |f|
|
616
|
+
fpath = Pathname.new(f)
|
617
|
+
next if fpath.directory?
|
618
|
+
tgt = "lib/#{subdir}/#{fpath.relative_path_from(path).to_posix}"
|
619
|
+
libs << [f, tgt]
|
620
|
+
end
|
398
621
|
end
|
399
622
|
end
|
400
623
|
|
401
624
|
# Detect additional DLLs
|
402
625
|
dlls = Ocra.autodll ? LibraryDetector.detect_dlls : []
|
403
626
|
|
404
|
-
executable =
|
627
|
+
executable = nil
|
628
|
+
if Ocra.output_override
|
629
|
+
executable = Ocra.output_override
|
630
|
+
else
|
631
|
+
executable = Ocra.files.first.basename.ext('.exe')
|
632
|
+
executable.append_to_filename!("-debug") if Ocra.debug
|
633
|
+
end
|
405
634
|
|
406
|
-
windowed = (Ocra.files
|
635
|
+
windowed = (Ocra.files.first.ext?('.rbw') || Ocra.force_windows) && !Ocra.force_console
|
407
636
|
|
408
|
-
|
637
|
+
Ocra.msg "Building #{executable}"
|
409
638
|
target_script = nil
|
410
639
|
OcraBuilder.new(executable, windowed) do |sb|
|
411
640
|
# Add explicitly mentioned files
|
641
|
+
Ocra.msg "Adding user-supplied source files"
|
412
642
|
Ocra.files.each do |file|
|
413
|
-
file =
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
target = File.join('src', relative_path(src_prefix, file))
|
643
|
+
file = src_prefix / file
|
644
|
+
if file.subpath?(Host.exec_prefix)
|
645
|
+
target = file.relative_path_from(Host.exec_prefix)
|
646
|
+
elsif file.subpath?(src_prefix)
|
647
|
+
target = SRCDIR / file.relative_path_from(src_prefix)
|
419
648
|
else
|
420
|
-
target =
|
649
|
+
target = SRCDIR / file.basename
|
421
650
|
end
|
422
651
|
|
423
652
|
target_script ||= target
|
424
653
|
|
425
|
-
if
|
654
|
+
if file.directory?
|
426
655
|
sb.ensuremkdir(target)
|
427
656
|
else
|
428
657
|
sb.createfile(file, target)
|
@@ -431,61 +660,68 @@ EOF
|
|
431
660
|
|
432
661
|
# Add the ruby executable and DLL
|
433
662
|
if windowed
|
434
|
-
rubyexe =
|
663
|
+
rubyexe = Host.rubyw_exe
|
435
664
|
else
|
436
|
-
rubyexe =
|
665
|
+
rubyexe = Host.ruby_exe
|
437
666
|
end
|
438
|
-
|
439
|
-
|
440
|
-
|
667
|
+
Ocra.msg "Adding ruby executable #{rubyexe}"
|
668
|
+
sb.createfile(Host.bindir / rubyexe, BINDIR / rubyexe)
|
669
|
+
if Host.libruby_so
|
670
|
+
sb.createfile(Host.bindir / Host.libruby_so, BINDIR / Host.libruby_so)
|
441
671
|
end
|
442
672
|
|
443
673
|
# Add detected DLLs
|
444
674
|
dlls.each do |dll|
|
445
|
-
|
446
|
-
|
675
|
+
Ocra.msg "Adding detected DLL #{dll}"
|
676
|
+
if dll.subpath?(Host.exec_prefix)
|
677
|
+
target = dll.relative_path_from(Host.exec_prefix)
|
447
678
|
else
|
448
|
-
target =
|
679
|
+
target = BINDIR / File.basename(dll)
|
449
680
|
end
|
450
681
|
sb.createfile(dll, target)
|
451
682
|
end
|
452
683
|
|
453
684
|
# Add extra DLLs specified on the command line
|
454
685
|
Ocra.extra_dlls.each do |dll|
|
455
|
-
|
686
|
+
Ocra.msg "Adding supplied DLL #{dll}"
|
687
|
+
sb.createfile(Host.bindir / dll, BINDIR / dll)
|
456
688
|
end
|
457
689
|
|
458
690
|
# Add gemspec files
|
691
|
+
@gemspecs = sort_uniq(@gemspecs)
|
459
692
|
@gemspecs.each do |gemspec|
|
460
|
-
if subpath?(
|
461
|
-
path =
|
693
|
+
if gemspec.subpath?(Host.exec_prefix)
|
694
|
+
path = gemspec.relative_path_from(Host.exec_prefix)
|
462
695
|
sb.createfile(gemspec, path)
|
463
|
-
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| subpath?(
|
464
|
-
path =
|
696
|
+
elsif defined?(Gem) and gemhome = Gem.path.find { |pth| gemspec.subpath?(pth) }
|
697
|
+
path = GEMHOMEDIR / gemspec.relative_path_from(gemhome)
|
465
698
|
sb.createfile(gemspec, path)
|
466
699
|
else
|
467
|
-
|
700
|
+
Ocra.fatal_error "Gem spec #{gemspec} does not exist in the Ruby installation. Don't know where to put it."
|
468
701
|
end
|
469
702
|
end
|
470
703
|
|
471
704
|
# Add loaded libraries (features, gems)
|
705
|
+
Ocra.msg "Adding library files"
|
472
706
|
libs.each do |path, target|
|
473
707
|
sb.createfile(path, target)
|
474
708
|
end
|
475
709
|
|
476
710
|
# Set environment variable
|
477
711
|
sb.setenv('RUBYOPT', ENV['RUBYOPT'] || '')
|
478
|
-
sb.setenv('RUBYLIB', load_path.map{|path|
|
479
|
-
sb.setenv('GEM_PATH',
|
480
|
-
|
481
|
-
#
|
482
|
-
extra_arg = Ocra.arg.map { |
|
483
|
-
|
484
|
-
|
712
|
+
sb.setenv('RUBYLIB', load_path.map{|path| path.to_native}.uniq.join(';'))
|
713
|
+
sb.setenv('GEM_PATH', (TEMPDIR_ROOT / GEMHOMEDIR).to_native)
|
714
|
+
|
715
|
+
# Add the opcode to launch the script
|
716
|
+
extra_arg = Ocra.arg.map { |arg| ' "' + arg.gsub(/\"/,'\\"') + '"' }.join
|
717
|
+
installed_ruby_exe = TEMPDIR_ROOT / BINDIR / rubyexe
|
718
|
+
launch_script = (TEMPDIR_ROOT / target_script).to_native
|
719
|
+
sb.postcreateprocess(installed_ruby_exe,
|
720
|
+
"#{rubyexe} \"#{launch_script}\"#{extra_arg}")
|
485
721
|
|
486
|
-
|
722
|
+
Ocra.msg "Compressing" unless not Ocra.lzma_mode
|
487
723
|
end
|
488
|
-
|
724
|
+
Ocra.msg "Finished building #{executable} (#{File.size(executable)} bytes)"
|
489
725
|
end
|
490
726
|
|
491
727
|
module LibraryDetector
|
@@ -508,21 +744,17 @@ EOF
|
|
508
744
|
end
|
509
745
|
|
510
746
|
handles = module_handle_buffer.unpack("I*")
|
511
|
-
handles.select{|
|
747
|
+
handles.select { |handle| handle > 0 }.map do |handle|
|
512
748
|
str = "\x00" * 256
|
513
|
-
|
514
|
-
str[0,
|
749
|
+
modulefilename_length = getmodulefilename.call(handle, str, str.size)
|
750
|
+
Ocra.Pathname(str[0,modulefilename_length])
|
515
751
|
end
|
516
752
|
end
|
517
753
|
|
518
754
|
def LibraryDetector.detect_dlls
|
519
755
|
loaded = loaded_dlls
|
520
|
-
exec_prefix =
|
521
|
-
loaded.select
|
522
|
-
Ocra.subpath?(path, exec_prefix) and
|
523
|
-
File.basename(path) =~ /\.dll$/i and
|
524
|
-
File.basename(path).downcase != RbConfig::CONFIG['LIBRUBY_SO'].downcase
|
525
|
-
end
|
756
|
+
exec_prefix = Host.exec_prefix
|
757
|
+
loaded.select { |path| path.subpath?(exec_prefix) && path.basename.ext?('.dll') && path.basename != Host.libruby_so }
|
526
758
|
end
|
527
759
|
end
|
528
760
|
|
@@ -530,15 +762,32 @@ EOF
|
|
530
762
|
# (createfile, mkdir etc) are added by invoking methods on an
|
531
763
|
# instance of OcraBuilder.
|
532
764
|
class OcraBuilder
|
765
|
+
Signature = [0x41, 0xb6, 0xba, 0x4e]
|
766
|
+
OP_END = 0
|
767
|
+
OP_CREATE_DIRECTORY = 1
|
768
|
+
OP_CREATE_FILE = 2
|
769
|
+
OP_CREATE_PROCESS = 3
|
770
|
+
OP_DECOMPRESS_LZMA = 4
|
771
|
+
OP_SETENV = 5
|
772
|
+
OP_POST_CREATE_PROCESS = 6
|
773
|
+
OP_ENABLE_DEBUG_MODE = 7
|
774
|
+
OP_CREATE_INST_DIRECTORY = 8
|
775
|
+
|
533
776
|
def initialize(path, windowed)
|
534
777
|
@paths = {}
|
778
|
+
@files = {}
|
535
779
|
File.open(path, "wb") do |ocrafile|
|
536
|
-
|
780
|
+
image = nil
|
537
781
|
if windowed
|
538
|
-
|
782
|
+
image = Ocra.stubwimage
|
539
783
|
else
|
540
|
-
|
784
|
+
image = Ocra.stubimage
|
541
785
|
end
|
786
|
+
|
787
|
+
unless image
|
788
|
+
Ocra.fatal_error "Stub image not available"
|
789
|
+
end
|
790
|
+
ocrafile.write(image)
|
542
791
|
end
|
543
792
|
|
544
793
|
if Ocra.icon_filename
|
@@ -555,6 +804,12 @@ EOF
|
|
555
804
|
@of = ocrafile
|
556
805
|
end
|
557
806
|
|
807
|
+
if Ocra.debug
|
808
|
+
ocrafile.write([OP_ENABLE_DEBUG_MODE].pack("V"))
|
809
|
+
end
|
810
|
+
|
811
|
+
createinstdir Ocra.debug_extract, !Ocra.debug_extract
|
812
|
+
|
558
813
|
yield(self)
|
559
814
|
|
560
815
|
if Ocra.lzma_mode
|
@@ -563,7 +818,6 @@ EOF
|
|
563
818
|
system("\"#{Ocra.lzmapath}\" e tmpin tmpout 2>NUL") or fail
|
564
819
|
compressed_data = File.open("tmpout", "rb") { |tmp| tmp.read }
|
565
820
|
ocrafile.write([OP_DECOMPRESS_LZMA, compressed_data.size, compressed_data].pack("VVA*"))
|
566
|
-
ocrafile.write([OP_END].pack("V"))
|
567
821
|
ensure
|
568
822
|
File.unlink("tmpin") if File.exist?("tmpin")
|
569
823
|
File.unlink("tmpout") if File.exist?("tmpout")
|
@@ -579,38 +833,47 @@ EOF
|
|
579
833
|
end
|
580
834
|
|
581
835
|
def mkdir(path)
|
582
|
-
@paths[path.downcase]
|
583
|
-
|
584
|
-
|
836
|
+
return if @paths[path.path.downcase]
|
837
|
+
@paths[path.path.downcase] = true
|
838
|
+
Ocra.verbose_msg "m #{showtempdir path}"
|
839
|
+
@of << [OP_CREATE_DIRECTORY, path.to_native].pack("VZ*")
|
585
840
|
end
|
586
841
|
|
587
842
|
def ensuremkdir(tgt)
|
588
|
-
|
589
|
-
if
|
590
|
-
|
843
|
+
tgt = Ocra.Pathname(tgt)
|
844
|
+
return if tgt.path == "."
|
845
|
+
if not @paths[tgt.to_posix.downcase]
|
846
|
+
ensuremkdir(tgt.dirname)
|
591
847
|
mkdir(tgt)
|
592
848
|
end
|
593
849
|
end
|
594
850
|
|
851
|
+
def createinstdir(next_to_exe = false, delete_after = false)
|
852
|
+
@of << [OP_CREATE_INST_DIRECTORY, next_to_exe ? 1 : 0, delete_after ? 1 : 0].pack("VVV")
|
853
|
+
end
|
854
|
+
|
595
855
|
def createfile(src, tgt)
|
596
|
-
|
856
|
+
return if @files[tgt]
|
857
|
+
@files[tgt] = true
|
858
|
+
src, tgt = Ocra.Pathname(src), Ocra.Pathname(tgt)
|
859
|
+
ensuremkdir(tgt.dirname)
|
597
860
|
str = File.open(src, "rb") { |file| file.read }
|
598
|
-
|
599
|
-
@of << [OP_CREATE_FILE,
|
861
|
+
Ocra.verbose_msg "a #{showtempdir tgt}"
|
862
|
+
@of << [OP_CREATE_FILE, tgt.to_native, str.size, str].pack("VZ*VA*")
|
600
863
|
end
|
601
864
|
|
602
865
|
def createprocess(image, cmdline)
|
603
|
-
|
604
|
-
@of << [OP_CREATE_PROCESS,
|
866
|
+
Ocra.verbose_msg "l #{showtempdir image} #{showtempdir cmdline}"
|
867
|
+
@of << [OP_CREATE_PROCESS, image.to_native, cmdline].pack("VZ*Z*")
|
605
868
|
end
|
606
869
|
|
607
870
|
def postcreateprocess(image, cmdline)
|
608
|
-
|
609
|
-
@of << [OP_POST_CREATE_PROCESS,
|
871
|
+
Ocra.verbose_msg "p #{showtempdir image} #{showtempdir cmdline}"
|
872
|
+
@of << [OP_POST_CREATE_PROCESS, image.to_native, cmdline].pack("VZ*Z*")
|
610
873
|
end
|
611
874
|
|
612
875
|
def setenv(name, value)
|
613
|
-
|
876
|
+
Ocra.verbose_msg "e #{name} #{showtempdir value}"
|
614
877
|
@of << [OP_SETENV, name, value].pack("VZ*Z*")
|
615
878
|
end
|
616
879
|
|
@@ -619,9 +882,9 @@ EOF
|
|
619
882
|
end
|
620
883
|
|
621
884
|
def showtempdir(x)
|
622
|
-
x.gsub(
|
885
|
+
x.to_s.gsub(TEMPDIR_ROOT, "<tempdir>")
|
623
886
|
end
|
624
|
-
|
887
|
+
|
625
888
|
end # class OcraBuilder
|
626
889
|
|
627
890
|
end # module Ocra
|
@@ -630,19 +893,20 @@ if File.basename(__FILE__) == File.basename($0)
|
|
630
893
|
Ocra.init(ARGV)
|
631
894
|
ARGV.replace(Ocra.arg)
|
632
895
|
|
633
|
-
if
|
634
|
-
|
635
|
-
exit
|
896
|
+
if not Ocra.files.first.exist?
|
897
|
+
Ocra.fatal_error "#{Ocra.files[0]} was not found!"
|
636
898
|
end
|
637
899
|
|
638
900
|
at_exit do
|
639
901
|
if $!.nil? or $!.kind_of?(SystemExit)
|
640
902
|
Ocra.build_exe
|
641
|
-
exit
|
903
|
+
exit 0
|
642
904
|
end
|
643
905
|
end
|
644
906
|
|
645
|
-
|
646
|
-
|
647
|
-
|
907
|
+
if Ocra.run_script
|
908
|
+
Ocra.msg "Loading script to check dependencies"
|
909
|
+
$0 = Ocra.files.first
|
910
|
+
load Ocra.files.first
|
911
|
+
end
|
648
912
|
end
|