Skip to content

Commit 5b0a8d2

Browse files
committed
symlink aliases instead of copying them on POSIX
1 parent e5bb63c commit 5b0a8d2

File tree

9 files changed

+133
-48
lines changed

9 files changed

+133
-48
lines changed

README.md

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -69,66 +69,57 @@ dependencies (gems and DLLs) into an executable named
6969

7070
ocran --help
7171

72-
Ocran options:
72+
#### Ocran options:
7373

74-
--help Display this information.
75-
--quiet Suppress output while building executable.
76-
--verbose Show extra output while building executable.
77-
--version Display version number and exit.
74+
* `--help`: Display available command-line options and information.
75+
* `--quiet`: Suppress all output during the build process.
76+
* `--verbose`: Provide detailed output during the build process, useful for debugging.
77+
* `--version`: Display the version number of OCRAN and exit.
7878

79-
Packaging options:
79+
#### Packaging options:
8080

81-
--dll dllname Include additional DLLs from the Ruby bindir.
82-
--add-all-core Add all core ruby libraries to the executable.
83-
--gemfile <file> Add all gems and dependencies listed in a Bundler Gemfile.
84-
--no-enc Exclude encoding support files
81+
* `--dll <dllname>`: Include additional DLLs from the Ruby `bin` directory.
82+
* `--add-all-core`: Add all standard Ruby core libraries to the executable.
83+
* `--gemfile <file>`: Include all gems and their dependencies listed in a specified Bundler `Gemfile`.
84+
* `--no-enc`: Exclude encoding support files to reduce the size of the executable.
8585

86-
Gem content detection modes:
86+
#### Gem content detection modes:
8787

88-
--gem-minimal[=gem1,..] Include only loaded scripts
89-
--gem-guess=[gem1,...] Include loaded scripts & best guess (DEFAULT)
90-
--gem-all[=gem1,..] Include all scripts & files
91-
--gem-full[=gem1,..] Include EVERYTHING
92-
--gem-spec[=gem1,..] Include files in gemspec (Does not work with Rubygems 1.7+)
88+
These options control which files from the included gems are added to the executable.
9389

94-
--[no-]gem-scripts[=..] Other script files than those loaded
95-
--[no-]gem-files[=..] Other files (e.g. data files)
96-
--[no-]gem-extras[=..] Extra files (README, etc.)
90+
* `--gem-minimal[=gem1,..]`: Include only the scripts that are actually loaded during the dependency run.
91+
* `--gem-guess[=gem1,..]`: Include loaded scripts and a "best guess" of other necessary files (DEFAULT).
92+
* `--gem-all[=gem1,..]`: Include all scripts and important files from the gem.
93+
* `--gem-full[=gem1,..]`: Include every single file found in the gem's directory.
94+
* `--gem-spec[=gem1,..]`: Include files listed in the gem's gemspec (Note: Compatibility issues with newer RubyGems).
9795

98-
Gem modes:
96+
You can also fine-tune content inclusion using these flags:
9997

100-
* *minimal*: loaded scripts
101-
* *guess*: loaded scripts and other files
102-
* *all*: loaded scripts, other scripts, other files (except extras)
103-
* *full*: Everything found in the gem directory
98+
* `--[no-]gem-scripts[=..]`: Include/exclude non-loaded script files (.rb, .rbw).
99+
* `--[no-]gem-files[=..]`: Include/exclude other files like data files.
100+
* `--[no-]gem-extras[=..]`: Include/exclude "extra" files like READMEs, tests, and C sources.
104101

105-
File groups:
102+
#### Auto-detection options:
106103

107-
* *scripts*: .rb/.rbw files
108-
* *extras*: C/C++ sources, object files, test, spec, README
109-
* *files*: all other files
104+
* `--no-dep-run`: Do not run the source script to check for dependencies. Use this if your script has side effects during load or if you are manually specifying all dependencies.
105+
* `--no-autoload`: Do not attempt to load or include `autoload`ed constants.
106+
* `--no-autodll`: Disable the automatic detection of runtime DLL dependencies.
110107

111-
Auto-detection options:
108+
#### Output options:
112109

113-
--no-dep-run Don't run script.rb to check for dependencies.
114-
--no-autoload Don't load/include script.rb's autoloads.
115-
--no-autodll Disable detection of runtime DLL dependencies.
110+
* `--output <file>`: Specify the name and path of the generated executable. Defaults to `./<scriptname>.exe`.
111+
* `--no-lzma`: Disable LZMA compression. This results in faster build times but a significantly larger executable.
112+
* `--innosetup <file>`: Use the specified Inno Setup script (.iss) to create a Windows installer instead of a single standalone executable.
116113

117-
Output options:
114+
#### Executable options:
118115

119-
--output <file> Name the exe to generate. Defaults to ./<scriptname>.exe.
120-
--no-lzma Disable LZMA compression of the executable.
121-
--innosetup <file> Use given Inno Setup script (.iss) to create an installer.
122-
123-
Executable options:
124-
125-
--windows Force Windows application (rubyw.exe)
126-
--console Force console application (ruby.exe)
127-
--chdir-first When exe starts, change working directory to app dir.
128-
--icon <ico> Replace icon with a custom one.
129-
--rubyopt <str> Set the RUBYOPT environment variable when running the executable
130-
--debug Executable will be verbose.
131-
--debug-extract Executable will unpack to local dir and not delete after.
116+
* `--windows`: Force the creation of a Windows GUI application (uses `rubyw.exe`).
117+
* `--console`: Force the creation of a console application (uses `ruby.exe`).
118+
* `--chdir-first`: Changes the working directory to the application's temporary extraction directory before the script starts.
119+
* `--icon <ico>`: Replace the default Ruby icon with a custom `.ico` file.
120+
* `--rubyopt <str>`: Set the `RUBYOPT` environment variable that will be used when the executable runs.
121+
* `--debug`: Enables verbose output when the generated executable is run.
122+
* `--debug-extract`: The executable will unpack its contents to a local directory and will not delete them after execution, which is helpful for troubleshooting extraction issues.
132123

133124

134125
### Compilation:

exe/ocran

100644100755
File mode changed.

lib/ocran/build_helper.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ def copy_to_bin(source, target)
4242
cp(source, BINDIR / target)
4343
end
4444

45+
def symlink_in_bin(target, link_name)
46+
verbose "symlink #{BINDIR / link_name} -> #{target}"
47+
symlink(BINDIR / link_name, target.to_s)
48+
end
49+
4550
def copy_to_gem(source, target)
4651
cp(source, GEMDIR / target)
4752
end

lib/ocran/direction.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ def construct(builder)
150150
libruby_src = Gem.win_platform? ? bindir / libruby_so : libdir / libruby_so
151151
builder.copy_to_bin(libruby_src, libruby_so)
152152

153-
# On POSIX systems, we also need to copy symlinks (aliases) for libruby.so
153+
# On POSIX systems, create symlinks (aliases) for libruby.so
154154
unless Gem.win_platform?
155155
libruby_aliases.each do |libruby_alias|
156-
builder.copy_to_bin(libdir / libruby_alias, libruby_alias)
156+
builder.symlink_in_bin(libruby_so, libruby_alias)
157157
end
158158
end
159159
end

lib/ocran/stub_builder.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class StubBuilder
1313
OP_CREATE_FILE = 2
1414
OP_SETENV = 3
1515
OP_SET_SCRIPT = 4
16+
OP_CREATE_SYMLINK = 5
1617

1718
DEBUG_MODE = 0x01
1819
EXTRACT_TO_EXE_DIR = 0x02
@@ -154,6 +155,12 @@ def mkdir(target)
154155
write_path(target)
155156
end
156157

158+
def symlink(link_path, target)
159+
write_opcode(OP_CREATE_SYMLINK)
160+
write_path(link_path)
161+
write_string(target.to_s)
162+
end
163+
157164
def cp(source, target)
158165
unless File.exist?(source)
159166
raise "The file does not exist (#{source})"

src/inst_dir.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#include <stdlib.h>
22
#include <string.h>
3+
#ifndef _WIN32
4+
#include <unistd.h>
5+
#include <errno.h>
6+
#endif
37
#include "error.h"
48
#include "system_utils.h"
59
#include "inst_dir.h"
@@ -263,6 +267,53 @@ bool ExportFileToInstDir(const char *rel_path, const void *buf, size_t len)
263267
return result;
264268
}
265269

270+
#ifndef _WIN32
271+
bool CreateSymlinkUnderInstDir(const char *rel_link_path, const char *target)
272+
{
273+
if (!IsInstDirSet()) {
274+
APP_ERROR("Installation directory has not been set");
275+
return false;
276+
}
277+
278+
if (!rel_link_path || !*rel_link_path) {
279+
APP_ERROR("rel_link_path is NULL or empty");
280+
return false;
281+
}
282+
283+
if (!target || !*target) {
284+
APP_ERROR("target is NULL or empty");
285+
return false;
286+
}
287+
288+
char *link_path = ExpandInstDirPath(rel_link_path);
289+
if (!link_path) {
290+
return false;
291+
}
292+
293+
bool result = false;
294+
char *parent = GetParentPath(link_path);
295+
if (parent && *parent && !CreateDirectoriesRecursively(parent)) {
296+
APP_ERROR("Failed to create parent directory for symlink '%s'", link_path);
297+
goto cleanup;
298+
}
299+
300+
if (symlink(target, link_path) < 0) {
301+
APP_ERROR("Failed to create symlink '%s' -> '%s': %s", link_path, target, strerror(errno));
302+
goto cleanup;
303+
}
304+
305+
DEBUG("CreateSymlinkUnderInstDir: '%s' -> '%s'", link_path, target);
306+
result = true;
307+
308+
cleanup:
309+
if (parent) {
310+
free(parent);
311+
}
312+
free(link_path);
313+
return result;
314+
}
315+
#endif /* _WIN32 */
316+
266317
bool SetEnvWithInstDir(const char *name, const char *value)
267318
{
268319
char *replaced_value = ReplaceInstDirPlaceholder(value);

src/inst_dir.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,17 @@ bool ExportFileToInstDir(const char *rel_path, const void *buf, size_t len);
141141
* @return true on success; false on failure.
142142
*/
143143
bool SetEnvWithInstDir(const char *name, const char *value);
144+
145+
/**
146+
* @brief Create a symlink under the installation directory (POSIX only).
147+
*
148+
* Creates a symbolic link at rel_link_path that points to target.
149+
* Both paths are relative to the installation directory.
150+
* The target is treated as a bare filename within the same directory.
151+
*
152+
* @param rel_link_path Relative path for the new symlink (e.g. "bin/libruby.so").
153+
* @param target Symlink target filename (e.g. "libruby.so.3.2.0").
154+
*
155+
* @return true on success; false on failure.
156+
*/
157+
bool CreateSymlinkUnderInstDir(const char *rel_link_path, const char *target);

src/unpack.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ static bool process_opcode(UnpackReader *reader, Opcode opcode)
153153
return SetScriptInfo(args, size);
154154
}
155155

156+
case OP_CREATE_SYMLINK: {
157+
if (!read_string(reader, &name)) {
158+
return false;
159+
}
160+
if (!read_string(reader, &value)) {
161+
return false;
162+
}
163+
DEBUG("OP_CREATE_SYMLINK: link='%s', target='%s'", name, value);
164+
#ifndef _WIN32
165+
return CreateSymlinkUnderInstDir(name, value);
166+
#else
167+
DEBUG("OP_CREATE_SYMLINK: skipped on Windows");
168+
return true;
169+
#endif
170+
}
171+
156172
default: {
157173
DEBUG("Invalid opcode: %d", opcode);
158174
return false;

src/unpack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ typedef enum {
55
OP_CREATE_FILE = 2,
66
OP_SETENV = 3,
77
OP_SET_SCRIPT = 4,
8+
OP_CREATE_SYMLINK = 5,
89
} Opcode;
910

1011
/**

0 commit comments

Comments
 (0)