ocran 1.4.6 → 1.5.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.txt +82 -0
  3. data/README.md +519 -15
  4. data/ext/extconf.rb +5 -1
  5. data/lib/ocran/aot_toolchain.rb +154 -0
  6. data/lib/ocran/build_helper.rb +7 -0
  7. data/lib/ocran/command_output.rb +8 -1
  8. data/lib/ocran/compatibility_scan.rb +414 -0
  9. data/lib/ocran/cosmo_toolchain.rb +29 -7
  10. data/lib/ocran/dir_builder.rb +88 -38
  11. data/lib/ocran/direction.rb +292 -44
  12. data/lib/ocran/ed_resource.rb +113 -0
  13. data/lib/ocran/ed_resource_builder.rb +199 -0
  14. data/lib/ocran/env_hook.rb +98 -0
  15. data/lib/ocran/file_path_set.rb +9 -5
  16. data/lib/ocran/gem_spec_queryable.rb +13 -5
  17. data/lib/ocran/host_config_helper.rb +11 -1
  18. data/lib/ocran/inno_setup_script_builder.rb +24 -14
  19. data/lib/ocran/launcher_batch_builder.rb +8 -11
  20. data/lib/ocran/npm_package.rb +106 -0
  21. data/lib/ocran/option.rb +465 -35
  22. data/lib/ocran/pe_imports.rb +96 -0
  23. data/lib/ocran/pe_resources.rb +226 -0
  24. data/lib/ocran/refine_pathname.rb +29 -32
  25. data/lib/ocran/roundhouse_builder.rb +316 -0
  26. data/lib/ocran/runner.rb +52 -11
  27. data/lib/ocran/runtime_sdk.rb +259 -0
  28. data/lib/ocran/spinel_builder.rb +183 -0
  29. data/lib/ocran/spinel_compatibility.rb +149 -0
  30. data/lib/ocran/static_runtime/boot.rb +443 -0
  31. data/lib/ocran/static_runtime/build_static_ext.rb +86 -0
  32. data/lib/ocran/static_runtime_builder.rb +814 -0
  33. data/lib/ocran/stub_builder.rb +70 -25
  34. data/lib/ocran/version.rb +1 -1
  35. data/lib/ocran/wasm_builder.rb +570 -0
  36. data/lib/ocran/wasm_compatibility.rb +198 -0
  37. data/lib/ocran/windows_command_escaping.rb +21 -0
  38. data/lib/ocran/zip_payload_builder.rb +18 -9
  39. data/lib/ocran/zip_writer.rb +48 -9
  40. data/src/Makefile +18 -2
  41. data/src/Makefile.mswin +65 -0
  42. data/src/inst_dir.c +34 -6
  43. data/src/path_utils.c +144 -0
  44. data/src/script_info.c +5 -0
  45. data/src/stub.c +77 -19
  46. data/src/stub.rc +1 -1
  47. data/src/system_utils.c +276 -155
  48. data/src/system_utils.h +46 -3
  49. data/src/system_utils_posix.c +243 -148
  50. data/src/unpack.c +9 -0
  51. metadata +25 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9898f2fb5230aa7ee4f9bec0b47da2609bed63b37042b7ccb2b0dce9a694f25b
4
- data.tar.gz: a3ae88ab734250742900487fa9a4dd3b862da930f11c9f656959b197efc7d24c
3
+ metadata.gz: da09ddcc836fd862e4b8832daedf24ea3dc8c122779d19528e0d5611ef4b47ff
4
+ data.tar.gz: c231264021b94d991f197f9520ec5403445756707c0aca60c1841a72332754df
5
5
  SHA512:
6
- metadata.gz: 94b73edf3033cbda84a6d8f5d446b23acdffac467ac06c2881821c3caccb479bf5673e69662c96735d17cc2bfc0db43d0b44a0613e094c1e4251eac3cf915919
7
- data.tar.gz: a6fb23433b4fca4ed3379a8486b6202a7c1c0272799034ee28543ab8c137f7d3fd8b04ad23f1a87f2012f98ec65505297e6bc51a90247426676170a1cc909151
6
+ metadata.gz: bdc488641cfdca101ebd4e2b01168838fd15d3eb418606ebaf2b2af431a08fa9e92da3ecf959ee0a485f669ea79af0707e842396011916d37365f8e553eeadcf
7
+ data.tar.gz: ae524b14a5b1198a8f9f761b6e38ee0af06cb6843827b10dade4d9fb8d37585066cee7866ca3fa554f03825f7293a92f32bed0e28cea0380cc3ee5d3f7062154
data/CHANGELOG.txt CHANGED
@@ -1,3 +1,85 @@
1
+ === 1.5.1
2
+ - Releases carry the three `--static-runtime` runtimes (Windows zip, Linux and macOS tarballs) next to the gems: the release workflow calls the runtime workflows, which build, test and attest them, and attaches what they built (a published release is immutable, so they cannot be added afterwards). A manual run of the release workflow is a dry run that builds and tests everything a release would contain and publishes nothing. The runtime workflows run on branch pushes only; for a tag the release workflow runs them.
3
+ - The macOS runtime and macOS executables are reproducible: the runtime build sets ZERO_AR_DATE=1 (Apple's ar, libtool and ranlib wrote each member's modification time into the static libraries, and ld the objects' times into the executable's debug map), and OCRAN links macOS executables with -Wl,-S, without a debug map naming objects compiled in a temporary directory. The runtime workflows package the smoke application twice and require the same bytes.
4
+ - A bare bin/ruby(.exe) of a packaged application gets the launch script's environment: OCRAN packs a RubyGems startup hook (rubygems/defaults/operating_system.rb in the packed site_ruby) that sets the variables the launch script exports (GEM_HOME, GEM_PATH, RUBYLIB, SSL_CERT_FILE/SSL_CERT_DIR when a CA bundle is packed, ...) when they are not set, with paths relative to where the package is, and then loads the Ruby's own hook (RubyInstaller's). A Windows service or scheduled task registered as `bin\ruby.exe src\service.rb` - the way a service has to run, since the service control manager only accepts the process it started itself - could not load the packaged gems (LoadError) and used the account's own gem directory. New README section "Running as a Windows service".
5
+ - The standard library's unicode_normalize files are always packed. String#unicode_normalize is core but loads its implementation and tables from the standard library on first use, so an application that did not call it during the dependency run failed once packed with "cannot load such file -- unicode_normalize/normalize.rb".
6
+ - A build that needs a launcher stub which was never compiled (OCRAN used from a git checkout, e.g. a Bundler git source) fails at once with a message saying how to build it (`make -C src install`; `ridk exec make -C src install` with RubyInstaller) or, for directory and zip output, to pass --no-wrapper-exe; it used to fail with a bare ENOENT for share/ocran/stub.exe after the whole dependency run.
7
+ - `rake build` and `rake build_stub` work without the development bundle installed: the Rakefile sets up Bundler only for tasks other than building the stubs.
8
+ - --verbose reports "Failed to load gemspec" only for files in gem directories; it listed every standard library file the application loaded (hundreds of lines on Ruby 3.4), which buried real errors.
9
+ - The release workflow pushes every gem to rubygems.org with a Sigstore attestation (signed with sigstore-cli, as rubygems/release-gem does), so rubygems.org shows the gems as built and signed on GitHub Actions; the GitHub attestation alone does not reach rubygems.org.
10
+ - A library that exits while OCRAN loads its autoload constants no longer ends the build: it is reported as a warning, like an autoload that raises. rdoc/servlet.rb aborts when webrick is missing (Ruby 3.4 no longer ships it), so packaging an application that loaded rdoc failed with exit 1, and the abort message went to stderr ahead of the buffered progress output, at the start of the log rather than the end.
11
+ - Packaging no longer aborts with "No such file or directory" over an entry of $LOADED_FEATURES that has no file behind it. A racc parser generated with its runtime embedded marks <dir>/racc/parser.rb and racc/info.rb as loaded so that racc is not loaded twice; rdoc's RD parsers in Ruby 3.4 do, so an application that loaded them (directly or through a gem) failed with ".../lib/ruby/3.4.0/rdoc/rd/racc/parser.rb (Errno::ENOENT)" (reported on a Windows runner with Ruby 3.4.11 and ocran 1.4.6). Such entries are skipped; --verbose names them.
12
+ - `--add-all-core` no longer aborts over an entry of the Ruby library directories that is not a readable file, e.g. Debian's packaged Ruby, whose rdoc fonts are links into fonts-lato: it packs the rest and warns which entries it left out. A configured library directory that does not exist (Debian's sitelibdir, /usr/local/lib/site_ruby/<version>) is skipped; it failed with "Unexpected library path format".
13
+ - README: a one-line definition and quick start at the top, an FAQ, a comparison with other Ruby packagers and a section on security, dependencies and reproducibility; llms.txt summarises OCRAN for LLM-based search. The gemspec links the issue tracker and the README (bug_tracker_uri, documentation_uri) for rubygems.org.
14
+
15
+ === 1.5.0
16
+ - CI builds all three `--static-runtime` runtimes and tests them before publishing each as a workflow artifact: new static-runtime-windows.yml cross-builds the Windows runtime on Linux (Debian 13, in the official ruby image pinned by digest) and tests it on Windows (DLL imports, executables packaged on Linux run with a minimal PATH, packaging on Windows with nokogiri built by MSYS2's gcc). Each runtime is published as one reproducible archive (runtime/pack-runtime.rb; a zip for Windows, a .tar.gz for Linux and macOS), and a runtime that passed its tests gets a signed build provenance attestation for the archive and its runtime executable: `gh attestation verify <file> -R Largo/ocran`. runtime/smoke/run.rb runs executables with a minimal PATH and without Ruby's environment variables, and can run executables packaged elsewhere (OCRAN_SMOKE_PREBUILT=1).
17
+ - The release workflow attests every gem it publishes (signed build provenance): `gh attestation verify ocran-<version>-<platform>.gem -R Largo/ocran` checks a gem from the GitHub release or from rubygems.org (`gem fetch ocran`).
18
+ - Reproducible builds: the Windows stubs are linked without a build time in their PE header (-Wl,--no-insert-timestamp), so the same sources and toolchain give the same stub.exe, stubw.exe and platform gem; the source gem and the Linux stub already were. `--static-runtime` executables that link gem extensions are reproducible too: OCRAN runs the compiler, linker and strip with SOURCE_DATE_EPOCH set, which binutils' strip otherwise replaced with the current time in the PE header.
19
+ - `--static-runtime`: a runtime directory works after being moved or downloaded. The Linux SDK named the build host's kernel-header directory, which make-sdk.rb now copies into the SDK; the CI artifacts left out sdk/.ext (hidden), where Ruby's fake.rb finds ruby/config.h, so a downloaded runtime could not build gem extensions.
20
+ - `--static-runtime` builds gem extensions on a RubyInstaller host: RubyInstaller's RubyGems defaults, loaded when an extconf.rb requires rubygems (nokogiri's does), set up the build host's Ruby from the target's RbConfig and failed. Packaging nokogiri on Windows works.
21
+ - Error and warning messages come after the progress output that led to them when both go to one log: stdout is flushed first. They were printed before it, and a log excerpt could miss them.
22
+ - mswin Ruby support (x64-mswin64_140, the Ruby built with Visual C++ and vcpkg libraries, without MSYS2). The stubs build with Visual C++ through src/Makefile.mswin (nmake), which `rake build` and the source gem's extconf.rb use on mswin; they link the C runtime statically. The packed Ruby DLL of an mswin Ruby imports the Visual C++ runtime, which Windows does not ship, so OCRAN now bundles the VC++ runtime DLLs (vcruntime140*.dll, msvcp140*.dll, ...) the build process loaded into bin, next to ruby.exe; the executable runs where the VC++ Redistributable is not installed. The DLLs that ruby.exe, the Ruby DLL and the packed native extensions import from the Ruby bin directory (an mswin Ruby keeps gmp, libffi, OpenSSL, libyaml and zlib there) are read from their PE import tables (lib/ocran/pe_imports.rb) and packed even with --no-autodll; the interpreter does not start without gmp. Tested on Windows 11 with the official mswin build of Ruby 4.1.0dev and VS 2022 Build Tools, the executables run on a machine without the Redistributable.
23
+ - Windows: DLLs loaded through a junction, such as the "current" link of rbmanager or scoop, are resolved to their real path, so they are recognized as part of the Ruby installation that RbConfig describes.
24
+ - Windows stub: when ruby.exe dies in the loader (0xC0000135 DLL not found, 0xC0000139 entry point not found, 0xC000007B bad image, ...), the executable reports it instead of exiting silently with that status.
25
+ - Library paths of a development Ruby, whose version directory carries the ABI version (lib/ruby/4.1.0+5), are recognized; --add-all-core aborted with "Unexpected library path format" there.
26
+ - src/system_utils.c no longer includes ntstatus.h and ntdef.h, which redefine winnt.h types under the Windows SDK, and stub.rc quotes the icon file name, which rc.exe reads up to the hyphen.
27
+ - Linux/macOS: packaging works with a Ruby built without --enable-shared. Such a Ruby still reports LIBRUBY_SO (e.g. libruby.so.4.0.6) although only libruby-static.a is installed, and every build aborted with "The file does not exist (.../libruby.so.4.0.6)". When RbConfig's ENABLE_SHARED is "no" the interpreter is self-contained, and no libruby or libruby aliases are packed.
28
+ - `--static-runtime` supports macOS: runtime/macos/build-runtime.sh builds the runtime on a Mac with clang; it links only libraries of macOS itself (libSystem, libiconv, CoreFoundation). Since data appended to a Mach-O executable would break its code signature, OCRAN links every macOS executable at packaging time from the runtime's SDK with the application archive as the section __OCRAN,__zip, and the linker signs it ad hoc; `segedit app -extract __OCRAN __zip app.zip` gives the plain ZIP to audit. Packaging for macOS therefore needs a Mac. New CI workflows static-runtime-macos.yml and static-runtime-linux.yml build the macOS (arm64) and Linux runtimes and run runtime/smoke/ (a small application that checks file access, autoload, DATA, arguments, the exit status and that every loaded file comes from inside the executable, and HTTPS against the system trust store) with it.
29
+ - `--output` for a Windows `--static-runtime` executable gets .exe when the name has no extension, also when packaging on Linux or macOS.
30
+ - The ZIP directory entries OCRAN adds for parent directories (--static-runtime, --cosmo-ruby) carry the time of the file they belong to instead of the current time, so packing the same application twice gives the same bytes.
31
+ - `--static-runtime` (until now `--native`, renamed so that it is not confused with --spinel's native compilation; `--native-dll` is `--runtime-dll`, native/ is runtime/) supports Linux: runtime/linux/build-runtime.sh builds a fully static x86_64 Ruby linked against musl, which runs on any distribution (tested on Debian, Fedora, Alpine and BusyBox), and the executable's system follows from the runtime, not the build host. OpenSSL in the runtimes also loads the system's CA bundle on Linux and macOS (/etc/ssl/certs/ca-certificates.crt, /etc/pki/..., /etc/ssl/cert.pem), since a static OpenSSL knows only its compiled-in paths. nokogiri and tiny_tds work in the Linux runtime as well.
32
+ - New experimental `--static-runtime <runtime-dir>` option: build a single Windows executable that runs the application without extracting anything - no temporary directory, no DLLs written to disk. The output is a statically linked Ruby (a static runtime, built with runtime/build-runtime.sh on Linux or runtime/mswin/ with Visual Studio Build Tools) followed by the application as a plain ZIP archive, so `unzip -l app.exe` lists what will run and `ocran/manifest.sha256` inside it records the SHA-256 of the runtime and of every file. The runtime imports only DLLs that ship with Windows and needs no VC++ redistributable; ocran checks it against the hash in its runtime.json. The standard library and the gems the runtime ships are packed from the runtime, C extensions must be linked into it (anything else fails the build), and packing is byte-reproducible. Code: lib/ocran/static_runtime_builder.rb, lib/ocran/static_runtime/boot.rb (the read-only file layer the packed application runs on), runtime/.
33
+ - `--static-runtime` links the C extensions of the application's gems into the executable: an extension the runtime does not have is built from the gem's source against the runtime's SDK (`sdk/`, written by the runtime build) and linked into a runtime for this application, with MinGW (the Debian cross compiler on Linux, or RubyInstaller's MSYS2 GCC via OCRAN_RUNTIME_CC on Windows) or with Visual Studio Build Tools for an mswin runtime. Precompiled platform gems, which carry no source, are reported with a hint to install the source gem.
34
+ - `--static-runtime` runtimes (MinGW) carry static GNU libiconv, libxml2, libxslt and FreeTDS, so nokogiri and tiny_tds work: their C extensions are built against these libraries at packaging time. A precompiled platform gem (such as nokogiri's and tiny_tds' Linux builds) is built from the source gem of the same version, which OCRAN fetches from the configured gem sources into ~/.cache/ocran/gems (with the gems the source gem depends on that are not installed, such as mini_portile2 for nokogiri's extconf.rb, into ~/.cache/ocran/gems/build-gems, used only for building), and packed as a platform "ruby" gem without the binaries it carries for its platform. The runtime is Ruby 4.0.7.
35
+ - New `--runtime-dll <lib,...>` option: link C libraries of the runtime's SDK as DLLs shipped next to the executable instead of into it. The SDK has DLL builds of the LGPL libraries, GNU libiconv and FreeTDS (`--runtime-dll iconv,sybdb`), so that users can replace them as the LGPL asks; OCRAN copies each DLL and the SDK DLLs it imports.
36
+ - `--static-runtime` applies `--icon` and the version-resource options (`--set-version-string`, `--set-file-version`, `--set-product-version`, `--set-resource-string`, `--set-requested-execution-level`, `--application-manifest`) on every build host: lib/ocran/pe_resources.rb rewrites the resources of the runtime copy in Ruby, into a new section, instead of through the Windows-only UpdateResource API.
37
+ - `--static-runtime` executables contain the license texts of everything linked into the runtime as ocran/licenses/ inside the archive. runtime/LICENSES.md explains what static linking means for the licenses involved, the LGPL in particular.
38
+ - `--static-runtime`: OpenSSL in the runtime verifies certificates against the Windows certificate store (OpenSSL's winstore loader, added to OpenSSL::X509::Store#set_default_paths by a patch to Ruby's openssl extension), so HTTPS works without a CA file. The MinGW runtime's OpenSSL reads its configuration, certificates and engines from C:\Program Files\Common Files\SSL and C:\Program Files\OpenSSL instead of the build machine's prefix, which on Windows would have been a directory any user could create.
39
+ - ZipWriter writes a UTC entry time as given instead of converting it to the build host's local time, so a fixed time packs to the same bytes in every time zone. File times (local) are unchanged.
40
+ - Stub: OCRAN_DEBUG=0 (and an empty OCRAN_DEBUG) no longer switches debug output on; any other value still does. The CI workflow had set it to "0" to keep the stubs quiet and got every test's output interleaved with DEBUG lines.
41
+ - New experimental `--spinel` option: compile the script ahead of time with Spinel (https://github.com/matz/spinel) into a native executable that needs no Ruby, instead of packaging the interpreter. The script is not run at build time. Spinel compiles a subset of Ruby, so before compiling OCRAN scans the program statically with Prism: the script, the files it requires, and the source of every gem it requires. The scan supplies the `-I` roots Spinel needs for the project's lib/ and for pure-Ruby gems. When Spinel rejects the program, or is not installed, OCRAN prints a report grouped into the user's code and each gem. The report names gems with C extensions, standard libraries Spinel does not ship, unresolvable and computed requires, eval of strings, method_missing, computed define_method names, binding objects, ObjectSpace, TracePoint, refinements and Class.new. It also includes spinel-doctor's report when that tool is installed. `--spinel-opt <arg>` passes flags through to the compiler.
42
+ - New experimental `--roundhouse` option: compile a Rails application with Roundhouse (https://github.com/rubys/roundhouse) and Spinel into a native server binary. The output is a directory with the binary, its static/, public/, db/ and config/ files, and a storage/ directory that a rebuild keeps. On the first build the SQLite database is seeded when the sqlite3 command is available. When Roundhouse does not cover the app yet, OCRAN shows the gem census, the first errors and the unsupported-construct list from `roundhouse check --continue`. `--roundhouse-opt <arg>` passes flags through to Roundhouse.
43
+ - `--roundhouse` builds the generated project's static assets (`make assets`, with `BUNDLE_ONLY=assets` so only the Gemfile's assets group is installed) before `spin build`. Without them the binary served every stylesheet and script as a 404. It also puts the directory `pkg-config` reports for the allocator spin.toml names on LIBRARY_PATH for `spin build`, so Homebrew's jemalloc links on Apple Silicon, and a link failure now names the library it could not find.
44
+ - Both options find the compilers through SPINEL / SPIN / ROUNDHOUSE, then PATH, then the usual install locations, and print installation instructions for whichever is missing. Spinel does not support Windows natively yet, so on Windows the instructions point to WSL; nothing in OCRAN is tied to a platform, and the options will work on Windows once the compilers do.
45
+ - New experimental `--wasm[=ruby|picoruby]` option: export the application as a web page that runs it in the browser on WebAssembly. The result is a folder (`<script>-wasm`, or `--output-dir`) or a zip archive (`--output-zip`, or an `--output` name ending in .zip). It holds index.html, the runtime, and the program, and the page mirrors the program's standard output and errors. `--wasm` uses ruby.wasm (CRuby with its standard library). OCRAN packs the program into the prebuilt interpreter from npm with `rbwasm pack` (gem ruby_wasm, or RBWASM), which takes seconds; the pure-Ruby gems of the application's Gemfile.lock are packed in beside it at /gems with a setup file that stands in for bundler/setup. Only a gem with a C extension sends the build through `rbwasm build`, cached in ~/.cache/ocran/ruby_wasm. `--wasm-ruby <x.y>` picks the CRuby version and `--wasm-opt <arg>` passes flags to rbwasm. `--wasm=picoruby` (alias `--picoruby`) uses PicoRuby's 2 MB mruby-based runtime, which needs nothing installed; the program's own files are bundled into one script in require order, since PicoRuby cannot read them in the browser. The script is not run at build time. A static scan, shared with --spinel, reports what may not work on the chosen runtime: gems missing from the Gemfile or unavailable on PicoRuby, threads, processes, sockets, standard input, and CRuby standard libraries PicoRuby lacks. The runtimes come from the npm registry, are verified against its published sha512 integrity, and are cached in ~/.cache/ocran/npm. Zip archives are written with OCRAN's own ZipWriter, so no zip command is needed. The exports are tested against the real runtimes under Node (test/support/run_ruby_wasm.js, run_picoruby.js), in CI by the test-wasm job.
46
+ - Windows: new rcedit-style options set the PE resources of the generated executable and of the wrapper exe of --output-dir, --output-zip and --innosetup: `--set-version-string <key> <value>` (StringFileInfo entries such as CompanyName or ProductName), `--set-file-version` and `--set-product-version` (x.y.z.w), `--set-resource-string <id> <value>` (RT_STRING table), `--set-requested-execution-level` (manifest UAC level) and `--application-manifest <file>`, with the short aliases --version-string, --file-version, --product-version, --resource-string, --uac-level and --manifest. All are opt-in. The requested execution level replaces the level attribute of the manifest's requestedExecutionLevel element, also in a custom manifest, and leaves other level attributes and anything in comments or CDATA sections alone. docs/av-edr-false-positives.md explains how version information, an icon and code signing reduce antivirus and EDR false positives.
47
+ - Ruby 3.3 or newer is now required (was 3.2). Ruby 3.2 reached its end of life in March 2026 and was not tested in CI.
48
+ - A build that failed while writing an LZMA-compressed payload (the default) hung forever instead of reporting the error. The payload is written on a separate thread that feeds the compressor, and an exception there skipped closing the compressor's input, so the main thread waited for output that never came; the thread's exception was only printed. The compressor's input is now always closed and the error is raised in the main thread.
49
+ - Windows: the stub's cleanup of the extraction directory no longer follows junctions and directory symlinks. A link the application had created inside the directory (e.g. with --chdir-first) was recursed into, deleting the contents of the directory it pointed to outside the extraction directory; the link itself is now removed instead.
50
+ - Launch scripts from --output-dir/--output-zip pass build-time values literally. The .sh script embedded environment values and arguments in double quotes, so `$5` expanded to nothing and `$(cmd)` ran cmd at launch; they are now single-quoted. The .bat script set SCRIPT_DIR and the environment unquoted, so unpacking the application to a path containing `&` (e.g. C:\R&D\app) ran the rest of the path as a command; it now quotes every set, quotes each argument and doubles `%`. The Inno Setup launcher.bat doubles `%` as well, which cmd.exe otherwise read as the start of a variable reference.
51
+ - Stub: the parent directory of a path directly below the root is now the root ("/app" gives "/", "C:\app.exe" gives "C:\"), not an empty string or the drive-relative "C:". An executable at the root of the filesystem, common in containers, broke --chdir-exe-dir and the installer wrapper's run-in-exe-dir mode.
52
+ - `--gem-spec` works again. The gemspec lists its files relative to the gem directory, and they were used as given, so every build with the option failed with "Don't know where to put gemfile lib/...". The files are now taken from the gem directory, and listed files the installed gem does not have are skipped.
53
+ - A failed build no longer leaves a `.ocran_stub_<pid>_<time>` file in the output directory. The executable is written under that temporary name next to the output and renamed when complete; an error on the way left the partial file behind, and it is now deleted.
54
+ - `--cosmo-ruby` under `bundle exec`: the probe that asks the payload which features it can resolve itself now runs outside the build's bundle, like the payload query before it. It inherited BUNDLER_SETUP and BUNDLE_GEMFILE, so the payload died setting up the build host's bundle, the probe answered "none", and native gems the payload provides without a gemspec (cgi, pathname) were rejected as incompatible.
55
+ - `--output-zip` replaces an existing archive instead of adding to it. `zip -r` kept entries of the previous build that the new one no longer has, and Compress-Archive on Windows refused to overwrite the file. On POSIX the libruby.so aliases are stored as symlinks (`zip -y`) instead of three copies of the library, and zip no longer lists every file it adds. On Windows the paths reach PowerShell through the environment instead of inside a quoted string, which a `'` in the path (e.g. in the user name of the TEMP directory) broke.
56
+ - The launch script of `--output-dir`/`--output-zip` is named after the application's script even when RUBYOPT carries -I/-r paths that OCRAN translates. The script that runs is then the generated RUBYOPT launcher, and the launch script was named after it (e.g. `bundle exec ocran app.rb --output-dir out` wrote ocran-rubyopt-launcher.sh instead of app.sh).
57
+ - `--chdir-exe-dir` takes effect in the `--cosmo-ruby` ZIP packaging mode, where it was accepted and ignored: the application now starts in the directory containing the executable, as with `--chdir-first` in that mode.
58
+ - The launch scripts of `--output-dir`/`--output-zip` honor `--chdir-first` (change into the directory of the packed script) and `--chdir-exe-dir` (change into the script's own directory, where the application lives), like the executable and the wrapper executable next to them already did; both options used to be ignored there. The .bat script now starts with `setlocal`, so neither its variables nor the new directory change outlive it in the cmd.exe session that ran it.
59
+ - Invalid and conflicting command lines are rejected when they are parsed, before the dependency run, instead of being silently accepted: `--innosetup` with `--output-dir` or `--output-zip` (the directory or zip was not built), `--windows` with `--console` (console won), an unknown `--gem-<group>` (reported only after the whole dependency run) or a negated mode such as `--no-gem-full` (which acted as `--gem-full`), an option given without its argument (`--output`, `--output-dir`, `--output-zip`, `--bundle-id`, `--dll`, `--rubyopt`, `--cosmo`, ...; they were ignored, or reported as "file not found"), and an unknown option, which printed the usage and exited with status 0.
60
+ - `--no-dep-run` warns when nothing else names what the script needs: without `--add-all-core` no standard library is packed, and with `--gemfile` but without `--gem-all`/`--gem-full` the gems are packed without their scripts. README described both options as required, which they are not (a script that loads nothing packages fine), while `--help` did not mention them at all; both now say when they are needed.
61
+ - Windows: packed paths are deduplicated case- and separator-insensitively, as the file system treats them. The case-insensitive Pathname comparison OCRAN defines is a refinement, and refinements do not reach Hash, Set or Array#uniq, so the set of packed files, the gem file list and the check that keeps gem files from being packed a second time as plain features all compared paths case-sensitively; one file reached under two spellings could be packed twice. Behavior on Linux and macOS is unchanged.
62
+ - `--innosetup`: a `{` in a packed path is written as `{{` in the generated [Dirs] Name and [Files] DestDir/DestName entries, where Inno Setup otherwise reads it as the start of a constant such as {app}. A file whose name contains a brace always gets an explicit, escaped DestName.
63
+ - `--cosmo-ruby` ZIP packaging no longer mistakes an interpreter for a ZIP64 archive because the four bytes of the ZIP64 locator signature occur somewhere in its last 64 KiB; only a locator at its fixed position right before the end-of-central-directory record counts.
64
+ - `--cosmo-ruby` ZIP packaging checks the limits of the ZIP format without ZIP64 (65535 entries, 4 GiB) before it writes anything, instead of after the application had already been written into the executable, and refuses such an application right away.
65
+ - `--cosmo`: a compiled stub enters the cache in ~/.cache/ocran by an atomic rename. It was copied into place, so a build running at the same time, or any build after one that was interrupted mid-copy, could package a truncated stub.
66
+ - `--macosx-bundle`: the application name and `--bundle-id` are XML-escaped in the generated Info.plist. A name such as "R&D" produced a property list macOS could not parse.
67
+ - Build errors caused by the input or the build host - an ArgumentError (e.g. an empty argument after `--`, a file too large for the executable format) or a system call error (e.g. an output directory that cannot be created) - are reported as a one-line `ERROR:` message like other build errors, instead of an OCRAN backtrace. `--verbose` adds the backtrace; other exceptions, which indicate a bug, keep Ruby's full report.
68
+ - Stub: the path helpers IsCleanRelativePath, JoinPath and GetParentPath now live once in src/path_utils.c, built into the Windows, POSIX and cosmocc stubs alike. system_utils.c and system_utils_posix.c each carried a copy (the POSIX one with a dead `#ifdef _WIN32` branch), so a fix to one, like the root handling of GetParentPath, had to be made twice to reach every platform.
69
+ - `--cosmo` stubs running on Windows refuse payload names containing a backslash. The cosmopolitan stub checked names for "." and ".." segments splitting at "/" only, but Windows splits the resulting path at "\" as well, so a name like `..\x` passed as one harmless segment and was then written outside the extraction directory. OCRAN never puts a backslash in a name (cosmo stubs are only built on Linux and macOS hosts), so only a tampered executable is affected.
70
+ - Linux/macOS: stopping a packaged application with a signal now stops it and still deletes its extraction directory. The stub ignored SIGINT and SIGTERM for its whole life without passing them on, so `kill`, `docker stop` or a service manager stopping the executable did nothing to the application until the SIGKILL that followed also skipped the cleanup, leaving the extracted application behind in the temp directory; SIGHUP, from closing the terminal, killed the stub before its cleanup. The stub now forwards SIGINT, SIGTERM, SIGHUP and SIGQUIT sent to it to the application (signals a terminal generates reach the application directly and are not sent twice), waits for it, deletes the extraction directory and then dies of the same signal the application died of, so callers see a signal death as before rather than an exit code. As PID 1 of a container, where that is not possible, it exits with 128 + the signal number. Signals that were ignored when the executable started (e.g. SIGHUP under nohup) stay ignored for the application too; the stub used to reset SIGINT and SIGTERM to their default for it. On macOS the kernel does not say whether a signal came from a process or from the terminal, so there the stub forwards a signal when it is not in the foreground process group of a controlling terminal (a service, a container, a background job, CI); in an interactive foreground run only the terminal's own Ctrl+C and hangup, which reach the application directly, stop it.
71
+ - Windows: closing the console window of a running packaged application, or shutting down the system, no longer leaves its extraction directory behind. The stub's console control handler returned TRUE for every event, which for these events lets Windows terminate the stub right away, before its cleanup. The handler now waits (for at most 2.5 seconds, well within the 5 seconds Windows grants) for the application, which received the same event, ends it if it is still running, deletes the extraction directory and only then returns; if the event arrives while the stub is still extracting, the application is not started at all. Ctrl+C and Ctrl+Break are still left to the application, and a logoff event, which only services receive, whenever any user logs off, still does not stop anything. The --innosetup wrapper's directory (RUN_IN_EXE_DIR) is never deleted, from the handler either.
72
+ - Windows: killing a packaged application's executable (Task Manager, `taskkill /F`, a service manager or CI runner ending it) now ends the application too. The stub started ruby.exe as an independent process, which kept running without the stub, orphaned and with its extraction directory in use. The stub now puts ruby.exe into a job object that kills it when the stub goes away. Processes the application starts itself are not in that job and are not affected. When the stub already runs in a job that does not allow this, the application starts as before.
73
+ - Windows: read-only files and directories in the extraction directory, e.g. ones the application copied from a read-only source or a git checkout's object files, no longer stay behind after the application exits. DeleteFileW and RemoveDirectoryW refuse read-only entries, and the fallback of scheduling them for deletion at reboot needs administrator rights, so they, and every directory above them, were left in the temp directory. The stub now clears the read-only attribute first (never on links, whose attributes may be their target's), and reports a failure of anything below the directory instead of only of the directory itself.
74
+ - Linux/macOS: the stub's cleanup no longer gives up at the first entry of the extraction directory it cannot delete. It stopped deleting as soon as one entry failed, leaving everything not yet reached behind as well, and it could not enter directories the application had made read-only or unreadable (e.g. a copied Go module cache), so one such directory kept the whole extracted application in the temp directory. The stub now restores the owner's permissions on such directories, carries on past entries that still fail, and reports cleanup failures only in debug mode (they were printed as FATAL errors after the application had already run), as on Windows.
75
+ - Stub: when normalizing the new extraction directory's path to its long form fails (out of memory), the stub keeps the path it has instead of forgetting the directory. It had already been created, but its path was dropped, so nothing deleted it and it stayed in the temp directory.
76
+ - Linux/macOS: the stub only creates payload symlinks that point at a file in the same directory, and never writes an extracted file through a symlink. It created a link to whatever target the payload named, absolute or climbing out with "..", and then followed such links while creating directories and files, so a tampered executable could have files written anywhere its user can write. OCRAN itself only emits links between files in bin (shared library aliases such as libruby.so -> libruby.so.3.4.1), which keep working.
77
+ - Stub: an LZMA-compressed payload must now decompress to exactly the size its header states. The stub allocated the stated size and parsed all of it as opcodes, whatever the stream actually produced, so an executable whose stream ended early had uninitialized memory interpreted as extraction instructions. Such an executable now fails to start with "LZMA decompressed size mismatch" in the debug output. Executables built by OCRAN always match.
78
+ - Windows: the stub's directory creation can no longer loop forever. Looking for the deepest existing parent of a new directory, it cut the path back one segment at a time in a loop whose condition could never become false, so a path without any existing ancestor (a missing drive, or a relative path) made the executable hang with the CPU busy instead of failing; had it ended, it would also have replaced the first character of the path with a backslash. The walk now stops at the root and creates the first missing directory itself. A directory that already exists by the time it is created (another process was quicker, or a trailing separator) no longer counts as a failure either.
79
+ - Windows: the stub no longer takes a TEMP directory path too long for its 260-character buffer for a valid one. GetTempPathW then returns the size it would need and leaves the buffer undefined, and the stub went on with whatever the buffer held as the temp directory. It now asks for the required size first and fetches the whole path.
80
+ - Stub: running out of memory while preparing the application's command line now fails with an error instead of crashing on a NULL pointer, which also skipped deleting the extraction directory.
81
+ - Stub build: editing a header in src/ now rebuilds the objects that include it. The Makefile listed no header dependencies, so after a change to e.g. unpack.h an incremental `make` (or `rake build`) linked stale objects against new ones, which could produce a stub that disagreed with itself about structure layouts or opcode values until `make clean`. Every object now depends on the stub's headers (and the LZMA headers); explicit prerequisites rather than compiler-generated dependency files, which works the same with gcc, mingw and cosmocc.
82
+
1
83
  === 1.4.6
2
84
  - Applications no longer carry gems that only OCRAN itself activated. The gem scan read Gem.loaded_specs while the build was already running instead of the snapshot taken after the dependency run, so whatever OCRAN had activated by then was packed as if the application had loaded it: OCRAN's own gem and its fiddle dependency, activated by the `ocran` command of an installed OCRAN (since OCRA 1.3.2, which started packing every gem in Gem.loaded_specs; about 390 KB of stubs, lzma.exe and sources per application), and since 1.3.16, whose refactoring moved the build ahead of the gem scan, also tempfile, tmpdir, fileutils, delegate and etc from the stub builder and fiddle from DLL detection. The scan now uses the snapshot and leaves out the running OCRAN's gem and its runtime dependencies. Only activation is discounted: a gem the application does load is still packed. The same snapshot decides which gems' SxS assembly manifests are included on Windows.
3
85
  - Gems that Bundler installed into a configured path are found without Bundler (issue #61). ruby/setup-ruby with `bundler-cache: true` - the README's GitHub Actions example - installs the Gemfile's gems into vendor/bundle, a place plain RubyGems never looks, so `ocran app.rb` died with a LoadError on the first gem during the dependency run, `--gemfile` included. Before loading the script OCRAN now asks Bundler, in a child process and without evaluating the Gemfile, where the application's bundle is installed, and adds that directory to the gem path when it is not the system gems. The bundle is not set up, so gems the Gemfile does not list stay loadable as before; a bundle installed into the system gems and `bundle exec` are unaffected.