ocran 1.5.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +14 -0
- data/README.md +225 -6
- data/lib/ocran/build_helper.rb +7 -0
- data/lib/ocran/direction.rb +102 -14
- data/lib/ocran/env_hook.rb +98 -0
- data/lib/ocran/gem_spec_queryable.rb +6 -2
- data/lib/ocran/option.rb +40 -0
- data/lib/ocran/runner.rb +9 -0
- data/lib/ocran/runtime_sdk.rb +8 -1
- data/lib/ocran/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da09ddcc836fd862e4b8832daedf24ea3dc8c122779d19528e0d5611ef4b47ff
|
|
4
|
+
data.tar.gz: c231264021b94d991f197f9520ec5403445756707c0aca60c1841a72332754df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bdc488641cfdca101ebd4e2b01168838fd15d3eb418606ebaf2b2af431a08fa9e92da3ecf959ee0a485f669ea79af0707e842396011916d37365f8e553eeadcf
|
|
7
|
+
data.tar.gz: ae524b14a5b1198a8f9f761b6e38ee0af06cb6843827b10dade4d9fb8d37585066cee7866ca3fa554f03825f7293a92f32bed0e28cea0380cc3ee5d3f7062154
|
data/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
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
|
+
|
|
1
15
|
=== 1.5.0
|
|
2
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).
|
|
3
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`).
|
data/README.md
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
# OCRAN
|
|
1
|
+
# OCRAN — turn Ruby scripts into standalone executables (.exe) for Windows, Linux and macOS
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
[
|
|
5
|
+
[](https://rubygems.org/gems/ocran)
|
|
6
|
+
|
|
7
|
+
**OCRAN (One-Click Ruby Application Next) is a Ruby-to-executable packager:
|
|
8
|
+
`ocran app.rb` turns a Ruby script into a single `.exe` (Windows) or native
|
|
9
|
+
binary (Linux, macOS) that runs on machines without Ruby installed.** It is
|
|
10
|
+
the maintained successor of [OCRA](https://github.com/larsch/ocra) for
|
|
11
|
+
Ruby 3.3 and newer.
|
|
12
|
+
|
|
13
|
+
[OCRAN](https://github.com/largo/ocran) packages Ruby applications for
|
|
6
14
|
distribution. It bundles your script, the Ruby interpreter, gems, and native
|
|
7
15
|
libraries into a single self-contained artifact that runs without requiring
|
|
8
16
|
Ruby to be installed on the target machine.
|
|
9
17
|
|
|
18
|
+
Quick start:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
gem install ocran
|
|
22
|
+
ocran app.rb # → app.exe on Windows, ./app on Linux/macOS
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
See the [FAQ](#faq) and the [comparison with other Ruby packagers](#comparison-with-other-ruby-packagers).
|
|
26
|
+
|
|
10
27
|
OCRAN supports four output formats, all cross-platform:
|
|
11
28
|
|
|
12
29
|
* **Self-extracting executable** — bundles everything into a single binary that unpacks and runs transparently, with no Ruby installation required. Produces a `.exe` on Windows, and a native executable on Linux and macOS.
|
|
@@ -256,7 +273,8 @@ goes through these steps in order of impact.
|
|
|
256
273
|
built. CI builds and tests the three runtimes and publishes each as a
|
|
257
274
|
reproducible archive (a zip for Windows, a tarball otherwise) with a
|
|
258
275
|
signed build provenance attestation
|
|
259
|
-
(`gh attestation verify <archive> -R Largo/ocran`).
|
|
276
|
+
(`gh attestation verify <archive> -R Largo/ocran`); releases from 1.5.1
|
|
277
|
+
on have them attached.
|
|
260
278
|
|
|
261
279
|
Dependency detection runs as usual, under the build host's Ruby, which
|
|
262
280
|
must have the runtime's API version (e.g. 4.0.0). The standard library
|
|
@@ -643,6 +661,41 @@ On Windows, run the batch file:
|
|
|
643
661
|
|
|
644
662
|
myapp\script.bat
|
|
645
663
|
|
|
664
|
+
### Running as a Windows service
|
|
665
|
+
|
|
666
|
+
The self-extracting `.exe`, the wrapper `.exe` and the launch script all
|
|
667
|
+
start the packed `ruby.exe` as a child process. The Windows service control
|
|
668
|
+
manager accepts the service dispatcher only from the process it started
|
|
669
|
+
itself, so a service registered through any of them fails with error 1063
|
|
670
|
+
("The service process could not connect to the service controller").
|
|
671
|
+
|
|
672
|
+
Build with `--output-dir` (or `--innosetup`) instead and register the packed
|
|
673
|
+
interpreter directly, e.g. with `sc create` or the
|
|
674
|
+
[win32-service](https://github.com/chef/win32-service) gem's
|
|
675
|
+
`Win32::Service.create`:
|
|
676
|
+
|
|
677
|
+
ocran service.rb --output-dir C:\MyService
|
|
678
|
+
sc create MyService binPath= "C:\MyService\bin\ruby.exe C:\MyService\src\service.rb"
|
|
679
|
+
|
|
680
|
+
That bare `ruby.exe` still gets the package's environment: OCRAN packs a
|
|
681
|
+
RubyGems startup hook (`lib/ruby/site_ruby/<version>/rubygems/defaults/operating_system.rb`)
|
|
682
|
+
that sets `GEM_HOME`, `GEM_PATH`, `RUBYLIB`, `SSL_CERT_FILE` and the other
|
|
683
|
+
variables of the launch script to the directory's actual location, unless
|
|
684
|
+
they are already set, clears those the launch script clears
|
|
685
|
+
(`BUNDLE_GEMFILE`), and then loads the Ruby installation's own one
|
|
686
|
+
(RubyInstaller's). OCRAN leaves the hook out, and warns, when the building
|
|
687
|
+
Ruby has such a file in its `site_ruby` already or keeps `site_ruby` outside
|
|
688
|
+
its installation directory; then set those variables in the service's
|
|
689
|
+
environment yourself. The hook does not change the working directory (a service
|
|
690
|
+
starts in `C:\Windows\System32`), so find your files from `__dir__`. If the
|
|
691
|
+
script the launch script runs is a generated `ocran-rubyopt-launcher.rb`
|
|
692
|
+
(`-I`/`-r` in `RUBYOPT`), register that one.
|
|
693
|
+
|
|
694
|
+
OCRAN runs the script at build time, where the service's `mainloop` cannot
|
|
695
|
+
work: skip it with `defined?(Ocran)` (see "Detecting OCRAN at build time")
|
|
696
|
+
and `require` there what the service only loads later, such as `net/http`,
|
|
697
|
+
so that it gets packed.
|
|
698
|
+
|
|
646
699
|
### Pitfalls:
|
|
647
700
|
|
|
648
701
|
* Avoid modifying load paths at runtime. Use `-I` or `RUBYLIB` if needed, but don't expect OCRAN to preserve them for runtime. OCRAN may pack sources into other directories than you
|
|
@@ -1320,10 +1373,176 @@ actually encrypts stay out of reach until CosmoRuby's `openssl` grows a
|
|
|
1320
1373
|
cipher surface — mbedtls has AES, GCM and PKCS5, so it is a bounded job;
|
|
1321
1374
|
it is tracked in `PORTING-NOTES.md` in the CosmoRuby repository.
|
|
1322
1375
|
|
|
1323
|
-
##
|
|
1376
|
+
## FAQ
|
|
1377
|
+
|
|
1378
|
+
### How do I convert a Ruby script into an .exe?
|
|
1324
1379
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1380
|
+
Install OCRAN on Windows and run it on your entry script:
|
|
1381
|
+
|
|
1382
|
+
gem install ocran
|
|
1383
|
+
ocran app.rb
|
|
1384
|
+
|
|
1385
|
+
The result is `app.exe`, which contains your script, the Ruby interpreter,
|
|
1386
|
+
the gems it loaded and the DLLs they need. On Linux and macOS the same
|
|
1387
|
+
command produces a native executable `./app`.
|
|
1388
|
+
|
|
1389
|
+
### Does the target machine need Ruby installed?
|
|
1390
|
+
|
|
1391
|
+
No. The executable carries its own Ruby interpreter, standard library,
|
|
1392
|
+
gems and native libraries.
|
|
1393
|
+
|
|
1394
|
+
### Can I build a Windows .exe on Linux or macOS?
|
|
1395
|
+
|
|
1396
|
+
Not with the default mode: OCRAN bundles the Ruby of the machine it runs
|
|
1397
|
+
on, so build a Windows `.exe` on Windows (for example on a GitHub Actions
|
|
1398
|
+
Windows runner, see
|
|
1399
|
+
[Building for multiple platforms](#building-for-multiple-platforms-with-github-actions)),
|
|
1400
|
+
or run a Windows Ruby and OCRAN under Wine on Linux/macOS.
|
|
1401
|
+
Two experimental modes package without the target's own Ruby:
|
|
1402
|
+
`--static-runtime` with a Windows runtime builds a Windows `.exe` on
|
|
1403
|
+
Linux (and the other way round), and `--cosmo-ruby` packages a
|
|
1404
|
+
cosmopolitan Ruby into one file that runs on Windows, Linux and macOS.
|
|
1405
|
+
|
|
1406
|
+
### What is the difference between OCRA and OCRAN?
|
|
1407
|
+
|
|
1408
|
+
OCRAN is a maintained fork of OCRA. OCRA targets Windows only and is no
|
|
1409
|
+
longer updated for current Ruby versions; OCRAN supports Ruby 3.3+, builds
|
|
1410
|
+
for Windows, Linux and macOS, and adds directory, zip, macOS `.app` and
|
|
1411
|
+
Inno Setup output. To migrate, call `ocran` instead of `ocra` and replace
|
|
1412
|
+
`OCRA_EXECUTABLE` with `OCRAN_EXECUTABLE` in your code.
|
|
1413
|
+
|
|
1414
|
+
### Which gems and native extensions are supported?
|
|
1415
|
+
|
|
1416
|
+
OCRAN runs your script once at build time and records every file that
|
|
1417
|
+
`require` and `load` pull in, including compiled extensions and the DLLs
|
|
1418
|
+
or shared libraries they link. In the default mode, gems with native extensions
|
|
1419
|
+
(for example `sqlite3`, `pg`, `nokogiri`) are packaged along with their
|
|
1420
|
+
libraries; the experimental `--cosmo-ruby` mode only supports the native
|
|
1421
|
+
extensions built into its interpreter. Use
|
|
1422
|
+
`--gemfile` to include everything from a Bundler `Gemfile`, and the
|
|
1423
|
+
[gem detection modes](#gem-content-detection-modes) to include extra files.
|
|
1424
|
+
|
|
1425
|
+
### Can I package a Rails, GUI or command-line app?
|
|
1426
|
+
|
|
1427
|
+
Yes. Command-line tools work out of the box, GUI apps can use
|
|
1428
|
+
`--windows` to hide the console on Windows, and Rails apps with SQLite are
|
|
1429
|
+
covered in [Packaging a Rails application](#packaging-a-rails-application).
|
|
1430
|
+
|
|
1431
|
+
### Why does my packaged app start slowly?
|
|
1432
|
+
|
|
1433
|
+
The default self-extracting executable unpacks Ruby to a temporary
|
|
1434
|
+
directory on every launch. Use `--output-dir`, `--output-zip` or
|
|
1435
|
+
`--innosetup` to install the files once, `--static-runtime` for a single
|
|
1436
|
+
executable that runs without extracting, or `--cosmo-ruby` with an
|
|
1437
|
+
interpreter that supports ZIP packaging (CosmoRuby `v4.0.6-cosmo3` or
|
|
1438
|
+
newer) to run without extracting; with other interpreters `--cosmo-ruby`
|
|
1439
|
+
falls back to a launcher stub that extracts on every start.
|
|
1440
|
+
|
|
1441
|
+
### My antivirus flags the generated .exe. What can I do?
|
|
1442
|
+
|
|
1443
|
+
Self-extracting executables that unpack an interpreter are a common
|
|
1444
|
+
heuristic trigger. Sign the executable (OCRAN output is code-signing
|
|
1445
|
+
compatible, e.g. with `signtool` on Windows), ship an
|
|
1446
|
+
installer built with `--innosetup`, or ship a plain directory with
|
|
1447
|
+
`--output-dir`.
|
|
1448
|
+
|
|
1449
|
+
## Comparison with other Ruby packagers
|
|
1450
|
+
|
|
1451
|
+
| Tool | Output | Platforms | Single file | Maintained for current Ruby |
|
|
1452
|
+
|------|--------|-----------|-------------|--------------------------|
|
|
1453
|
+
| **OCRAN** | `.exe` / native binary, directory, zip, macOS `.app`, Inno Setup installer | Windows, Linux, macOS | Yes | Yes |
|
|
1454
|
+
| [OCRA](https://github.com/larsch/ocra) | `.exe` | Windows only | Yes | No |
|
|
1455
|
+
| [Traveling Ruby](https://github.com/trubygems/traveling-ruby) | Prebuilt Ruby runtime you ship next to your app | Windows, Linux, macOS | No | Yes |
|
|
1456
|
+
| [Tebako](https://github.com/tamatebako/tebako) | Single binary with an embedded filesystem | Windows, Linux, macOS | Yes | Yes |
|
|
1457
|
+
| [ruby-packer](https://github.com/pmq20/ruby-packer) | Single binary with Ruby compiled from source | Windows, Linux, macOS | Yes | No |
|
|
1458
|
+
|
|
1459
|
+
OCRAN's distinguishing features are that, in its default mode, it needs no
|
|
1460
|
+
C compiler to package an app (it reuses your installed Ruby), detects dependencies by running
|
|
1461
|
+
your script, and can produce installers and macOS app bundles directly.
|
|
1462
|
+
|
|
1463
|
+
See also: [State of Ruby Packagers](https://gist.github.com/YOU54F/3775e66e6090e0371c11601e6b75c305).
|
|
1464
|
+
|
|
1465
|
+
## Security, dependencies and reproducibility
|
|
1466
|
+
|
|
1467
|
+
A packager decides which interpreter, libraries and gems end up on your
|
|
1468
|
+
users' machines, so where those come from matters as much as the
|
|
1469
|
+
packaging itself. OCRAN is built so that nothing reaches the executable
|
|
1470
|
+
that you did not install, run and test yourself, and so that you can
|
|
1471
|
+
check what went in.
|
|
1472
|
+
|
|
1473
|
+
**No hidden downloads.** In its default mode OCRAN makes no network
|
|
1474
|
+
requests: it does not fetch an interpreter, prebuilt runtime, patched
|
|
1475
|
+
Ruby source or library tarball while packaging. The executables it
|
|
1476
|
+
produces, in every mode, never download a runtime when they start. Some
|
|
1477
|
+
packaging approaches ship a small launcher that pulls the actual runtime
|
|
1478
|
+
from the internet on first launch; the app then depends on a server
|
|
1479
|
+
being reachable and trustworthy long after you released it. An OCRAN
|
|
1480
|
+
executable carries the interpreter, gems and libraries it runs; whether
|
|
1481
|
+
your app uses the network is up to your own code. The opt-in modes that
|
|
1482
|
+
do fetch say so and verify what they get:
|
|
1483
|
+
|
|
1484
|
+
* `--wasm` downloads the ruby.wasm or PicoRuby package from the npm
|
|
1485
|
+
registry once, checks it against the SHA-512 integrity the registry
|
|
1486
|
+
publishes, and caches it, so later builds run offline.
|
|
1487
|
+
* `--static-runtime` fetches the source gem of a precompiled platform gem
|
|
1488
|
+
from your configured gem sources when it has to build that gem's
|
|
1489
|
+
extension for the runtime.
|
|
1490
|
+
|
|
1491
|
+
**Your Ruby, from the source you already trust.** By default OCRAN
|
|
1492
|
+
packages the Ruby interpreter installed on the build machine —
|
|
1493
|
+
RubyInstaller, a distribution package, `ruby-build`, `ruby/setup-ruby` in
|
|
1494
|
+
CI, or your own build. It does not substitute a separately built or
|
|
1495
|
+
patched interpreter from a third party. Security fixes reach your app the
|
|
1496
|
+
same way they reach your development machine: update Ruby, rebuild.
|
|
1497
|
+
Packagers that embed their own copy of Ruby and its C libraries (OpenSSL,
|
|
1498
|
+
zlib, libyaml, libffi, ...) freeze those versions at whatever the
|
|
1499
|
+
packager last shipped, and fall behind on security fixes when the
|
|
1500
|
+
packager stops being updated. The `--static-runtime` runtimes are built
|
|
1501
|
+
by scripts in this repository (`runtime/`) that you can run yourself;
|
|
1502
|
+
OCRAN checks a runtime against the SHA-256 recorded in its
|
|
1503
|
+
`runtime.json`.
|
|
1504
|
+
|
|
1505
|
+
**Exactly the gems and libraries you tested.** Dependencies are not
|
|
1506
|
+
resolved again at packaging time. OCRAN runs your script, records the
|
|
1507
|
+
files Ruby actually loaded, and packs those files from your installed
|
|
1508
|
+
gems — the versions pinned in your `Gemfile.lock` when you build under
|
|
1509
|
+
Bundler. Native libraries are taken from the same installation your
|
|
1510
|
+
extensions were loaded from. There is no second dependency resolution
|
|
1511
|
+
that could pick a different version than the one you ran.
|
|
1512
|
+
|
|
1513
|
+
**Reproducible builds.** The bundled interpreter and dependencies are
|
|
1514
|
+
determined by three things you control: the Ruby installation (or static
|
|
1515
|
+
runtime), the installed gems (or `Gemfile.lock`) and the OCRAN version;
|
|
1516
|
+
the rest of the artifact follows from your application source and the
|
|
1517
|
+
OCRAN options you pass. Pin them — e.g. a fixed `ruby-version` in
|
|
1518
|
+
`ruby/setup-ruby` plus a committed `Gemfile.lock` — and every build
|
|
1519
|
+
packages the same interpreter, gems and libraries. OCRAN's own stubs are
|
|
1520
|
+
built reproducibly (no timestamps in the PE header), the Windows, Linux
|
|
1521
|
+
and macOS static runtimes are reproducible, and `--static-runtime`
|
|
1522
|
+
executables are byte-reproducible on all three systems; CI packages a
|
|
1523
|
+
test application twice and requires the same bytes.
|
|
1524
|
+
|
|
1525
|
+
**Inspect what went in.** Build with `--output-dir` and look at the
|
|
1526
|
+
directory, or run an executable with `--debug-extract`. A
|
|
1527
|
+
`--static-runtime` executable is a plain ZIP archive behind the runtime:
|
|
1528
|
+
`unzip -l app.exe` lists exactly what will run, and
|
|
1529
|
+
`ocran/manifest.sha256` inside it records the SHA-256 of the runtime and
|
|
1530
|
+
of every file.
|
|
1531
|
+
|
|
1532
|
+
**Verifiable OCRAN releases.** The launcher stubs (`stub.exe`,
|
|
1533
|
+
`stubw.exe` and the Linux/macOS `stub`) are about 4,000 lines of C in
|
|
1534
|
+
`src/`, compiled from this repository by GitHub Actions. Released gems
|
|
1535
|
+
and the CI-built static runtimes carry signed build provenance
|
|
1536
|
+
attestations (`gh attestation verify <file> -R Largo/ocran`); from 1.5.1
|
|
1537
|
+
on, each GitHub release has the three runtimes attached next to the gems,
|
|
1538
|
+
and gems are pushed to rubygems.org with Sigstore attestations. The only
|
|
1539
|
+
third-party binary is `lzma.exe`, taken unmodified from the official
|
|
1540
|
+
7-Zip release (see [Safety](#safety)).
|
|
1541
|
+
|
|
1542
|
+
The trade-off: keeping the bundled Ruby patched is your job, just as it
|
|
1543
|
+
is for the Ruby you develop with. The experimental `--cosmo-ruby` mode
|
|
1544
|
+
packages a cosmopolitan interpreter file you choose, such as a CosmoRuby
|
|
1545
|
+
release, as given.
|
|
1327
1546
|
|
|
1328
1547
|
## Credits
|
|
1329
1548
|
|
data/lib/ocran/build_helper.rb
CHANGED
|
@@ -30,9 +30,16 @@ module Ocran
|
|
|
30
30
|
|
|
31
31
|
def export(name, value)
|
|
32
32
|
verbose "export #{name}=#{replace_placeholder(value)}"
|
|
33
|
+
exported_env[name.to_s] = value.to_s
|
|
33
34
|
super
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
# The variables exported so far (name => value with the extraction root
|
|
38
|
+
# placeholder), whatever the builder does with them.
|
|
39
|
+
def exported_env
|
|
40
|
+
@exported_env ||= {}
|
|
41
|
+
end
|
|
42
|
+
|
|
36
43
|
def replace_placeholder(s)
|
|
37
44
|
s.to_s.gsub(EXTRACT_ROOT.to_s, "<tempdir>")
|
|
38
45
|
end
|
data/lib/ocran/direction.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative "refine_pathname"
|
|
|
6
6
|
require_relative "host_config_helper"
|
|
7
7
|
require_relative "command_output"
|
|
8
8
|
require_relative "build_constants"
|
|
9
|
+
require_relative "env_hook"
|
|
9
10
|
|
|
10
11
|
module Ocran
|
|
11
12
|
class Direction
|
|
@@ -396,19 +397,30 @@ module Ocran
|
|
|
396
397
|
# Convert all relative paths to absolute paths before building.
|
|
397
398
|
# NOTE: In the future, different strategies may be needed before and after script execution.
|
|
398
399
|
features.filter_map do |feature|
|
|
399
|
-
|
|
400
|
-
feature
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
400
|
+
path =
|
|
401
|
+
if feature.absolute?
|
|
402
|
+
feature
|
|
403
|
+
elsif (load_path = @post_env.find_load_path(feature))
|
|
404
|
+
feature.expand_path(@post_env.expand_path(load_path))
|
|
405
|
+
else
|
|
406
|
+
# This message occurs when paths for core library files (e.g., enumerator.so,
|
|
407
|
+
# rational.so, complex.so, fiber.so, thread.rb, ruby2_keywords.rb) are not
|
|
408
|
+
# found. These are integral to Ruby's standard libraries or extensions and
|
|
409
|
+
# may not be located via normal load path searches, especially in RubyInstaller
|
|
410
|
+
# environments.
|
|
411
|
+
verbose "Load path not found for #{feature}, skip this feature"
|
|
412
|
+
next
|
|
413
|
+
end
|
|
414
|
+
# An entry can be in $LOADED_FEATURES without a file behind it: a
|
|
415
|
+
# racc parser generated with its runtime embedded marks
|
|
416
|
+
# <dir>/racc/parser.rb and racc/info.rb as loaded so that racc is not
|
|
417
|
+
# loaded twice (rdoc's RD parsers in Ruby 3.4 do). Copying it failed
|
|
418
|
+
# the whole build with ENOENT; there is nothing to pack.
|
|
419
|
+
unless path.file?
|
|
420
|
+
verbose "#{path} is marked as loaded but is not a file, skip this feature"
|
|
421
|
+
next
|
|
411
422
|
end
|
|
423
|
+
path
|
|
412
424
|
end
|
|
413
425
|
end
|
|
414
426
|
|
|
@@ -471,6 +483,19 @@ module Ocran
|
|
|
471
483
|
end
|
|
472
484
|
end
|
|
473
485
|
|
|
486
|
+
# String#unicode_normalize (and unicode_normalized?, unicode_normalize!)
|
|
487
|
+
# is core, but core requires its implementation from the standard
|
|
488
|
+
# library (unicode_normalize/normalize.rb, which loads tables.rb) on
|
|
489
|
+
# first use. An app that does not call it during the dependency run
|
|
490
|
+
# fails with a LoadError once packed, so always pack these files.
|
|
491
|
+
# Not with --cosmo-ruby: the payload has its own standard library.
|
|
492
|
+
unless @option.cosmo_ruby
|
|
493
|
+
known = features.to_set { |f| f.path_key }
|
|
494
|
+
(Pathname(RbConfig::CONFIG["rubylibdir"]) / "unicode_normalize").glob("*.rb").sort.each do |path|
|
|
495
|
+
features << path unless known.include?(path.path_key)
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
474
499
|
say "Building #{@option.output_executable}"
|
|
475
500
|
require_relative "build_helper"
|
|
476
501
|
builder.extend(BuildHelper)
|
|
@@ -955,14 +980,23 @@ module Ocran
|
|
|
955
980
|
elsif @option.add_all_core?
|
|
956
981
|
say "Will include all ruby core libraries"
|
|
957
982
|
all_core_dir.each do |path|
|
|
983
|
+
# A configured directory that was never created (Debian's
|
|
984
|
+
# sitelibdir, /usr/local/lib/site_ruby/<ver>) holds nothing.
|
|
985
|
+
next unless path.directory?
|
|
986
|
+
|
|
958
987
|
# Match the load path against standard library, site_ruby, and vendor_ruby paths
|
|
959
988
|
unless (subdir = path.to_posix.match(RUBY_LIBRARY_PATH_REGEX)&.[](1))
|
|
960
989
|
raise "Unexpected library path format (does not match core dirs): #{path}"
|
|
961
990
|
end
|
|
962
|
-
|
|
963
|
-
|
|
991
|
+
files, skipped = self.class.core_library_files(path)
|
|
992
|
+
files.each do |src|
|
|
964
993
|
builder.copy_to_lib(src, Pathname(subdir) / src.relative_path_from(path))
|
|
965
994
|
end
|
|
995
|
+
next if skipped.empty?
|
|
996
|
+
|
|
997
|
+
warning "Not packed: #{skipped.size} entr#{skipped.size == 1 ? "y" : "ies"} of #{path} that " \
|
|
998
|
+
"#{skipped.size == 1 ? "is" : "are"} not a readable file (#{skipped.first(3).join(", ")}" \
|
|
999
|
+
"#{skipped.size > 3 ? ", ..." : ""})"
|
|
966
1000
|
end
|
|
967
1001
|
end
|
|
968
1002
|
|
|
@@ -1215,6 +1249,8 @@ module Ocran
|
|
|
1215
1249
|
end
|
|
1216
1250
|
builder.set_env_path("GEM_PATH", *gem_paths)
|
|
1217
1251
|
|
|
1252
|
+
pack_env_hook(builder)
|
|
1253
|
+
|
|
1218
1254
|
# Add the opcode to launch the script
|
|
1219
1255
|
installed_ruby_exe = BINDIR / ruby_executable
|
|
1220
1256
|
target_script = builder.resolve_source_path(@option.script, inst_src_prefix)
|
|
@@ -1224,6 +1260,58 @@ module Ocran
|
|
|
1224
1260
|
builder.exec(installed_ruby_exe, target_script, *@option.argv)
|
|
1225
1261
|
end
|
|
1226
1262
|
|
|
1263
|
+
# Packs the RubyGems startup hook that sets the exported variables for a
|
|
1264
|
+
# bare bin/ruby of the package (see EnvHook), e.g. a Windows service
|
|
1265
|
+
# registered as `bin\ruby.exe src\service.rb`. It goes into the packed
|
|
1266
|
+
# sitelibdir, the first default load path entry, in every output that
|
|
1267
|
+
# carries the host Ruby: the stub and the launch scripts set the
|
|
1268
|
+
# variables themselves, so there it only loads the original file.
|
|
1269
|
+
# Not with --cosmo-ruby and --static-runtime, which bring their own
|
|
1270
|
+
# Ruby and set up their environment themselves.
|
|
1271
|
+
def pack_env_hook(builder)
|
|
1272
|
+
return if @option.cosmo_ruby || @option.static_runtime?
|
|
1273
|
+
|
|
1274
|
+
site = Pathname(sitelibdir)
|
|
1275
|
+
unless site.subpath?(exec_prefix)
|
|
1276
|
+
verbose "Not packing #{EnvHook::FEATURE}: sitelibdir #{site} is outside #{exec_prefix}"
|
|
1277
|
+
return
|
|
1278
|
+
end
|
|
1279
|
+
if (site / EnvHook::FEATURE).exist?
|
|
1280
|
+
warning "Not packing ocran's #{EnvHook::FEATURE}: #{site} has one already, so a bare " \
|
|
1281
|
+
"#{BINDIR / ruby_executable} of the package will not get the launch script's environment"
|
|
1282
|
+
return
|
|
1283
|
+
end
|
|
1284
|
+
|
|
1285
|
+
target = site.relative_path_from(exec_prefix) / EnvHook::FEATURE
|
|
1286
|
+
say "Adding #{target} (sets the launch environment for a bare #{BINDIR / ruby_executable})"
|
|
1287
|
+
require "tempfile"
|
|
1288
|
+
hook_file = Tempfile.new(["ocran-operating_system", ".rb"])
|
|
1289
|
+
hook_file.write(EnvHook.source(builder.exported_env, target.dirname.each_filename.count))
|
|
1290
|
+
hook_file.close
|
|
1291
|
+
verbose File.read(hook_file.path)
|
|
1292
|
+
# Kept until the build finishes, like the RUBYOPT launcher.
|
|
1293
|
+
@env_hook_file = hook_file
|
|
1294
|
+
builder.cp(hook_file.path, target)
|
|
1295
|
+
end
|
|
1296
|
+
private :pack_env_hook
|
|
1297
|
+
|
|
1298
|
+
# The files below +dir+ that --add-all-core packs, and the entries it
|
|
1299
|
+
# cannot: a Ruby installation can list what is not there - Debian's
|
|
1300
|
+
# rdoc links its fonts into a fonts-lato package that may not be
|
|
1301
|
+
# installed, and a stdlib entry that vanished or cannot be read used to
|
|
1302
|
+
# abort the whole build (ENOENT) over a file no application needs.
|
|
1303
|
+
# Returns [files, skipped], both lists of Pathname.
|
|
1304
|
+
def self.core_library_files(dir)
|
|
1305
|
+
files = []
|
|
1306
|
+
skipped = []
|
|
1307
|
+
dir.find do |src|
|
|
1308
|
+
next if src.directory?
|
|
1309
|
+
|
|
1310
|
+
(src.file? && src.readable? ? files : skipped) << src
|
|
1311
|
+
end
|
|
1312
|
+
[files, skipped]
|
|
1313
|
+
end
|
|
1314
|
+
|
|
1227
1315
|
# Maps an absolute build-machine path to its location (relative to the
|
|
1228
1316
|
# extraction root) inside the packed application, mirroring where
|
|
1229
1317
|
# #construct places files. Returns nil when the path is not packed.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative "build_constants"
|
|
3
|
+
|
|
4
|
+
module Ocran
|
|
5
|
+
# The RubyGems startup hook that gives a bare interpreter of a packed
|
|
6
|
+
# application the environment of its launch script.
|
|
7
|
+
#
|
|
8
|
+
# The launch script (app.bat / app.sh), the wrapper executable and the
|
|
9
|
+
# self-extracting stub all set the variables Direction#construct exports
|
|
10
|
+
# (GEM_HOME, GEM_PATH, RUBYLIB, SSL_CERT_FILE, ...) before they start
|
|
11
|
+
# bin/ruby. Some hosts can only start the interpreter itself: the Windows
|
|
12
|
+
# service control manager accepts the service dispatcher only from the
|
|
13
|
+
# process it started (error 1063), so a service registered as
|
|
14
|
+
# `bin\ruby.exe src\service.rb` runs without any of them - gems packed
|
|
15
|
+
# outside the Ruby installation are not found, and the CA bundle ocran
|
|
16
|
+
# packed is only used where OpenSSL happens to look on its own.
|
|
17
|
+
#
|
|
18
|
+
# RubyGems requires "rubygems/defaults/operating_system" while it loads,
|
|
19
|
+
# before it computes its paths and before anything of the application
|
|
20
|
+
# runs; distributions use that file to customise RubyGems (RubyInstaller
|
|
21
|
+
# sets up its DLL search paths in it). ocran packs its own file of that
|
|
22
|
+
# name in the packed sitelibdir, which comes first on the default load
|
|
23
|
+
# path. It sets each variable the launch script would set and that is
|
|
24
|
+
# not set yet, resolved against the directory the package is in at run
|
|
25
|
+
# time, and then requires the next operating_system.rb on the load path
|
|
26
|
+
# - the distribution's own - so that keeps working as before.
|
|
27
|
+
#
|
|
28
|
+
# Under the launch script or the stub every variable is already set, so
|
|
29
|
+
# the hook only passes on to the original file there.
|
|
30
|
+
module EnvHook
|
|
31
|
+
include BuildConstants
|
|
32
|
+
|
|
33
|
+
# The feature RubyGems requires at startup.
|
|
34
|
+
FEATURE = "rubygems/defaults/operating_system.rb"
|
|
35
|
+
|
|
36
|
+
# Builds the hook's source for the variables in +env+ (name => value as
|
|
37
|
+
# exported at build time, with the extraction root placeholder) for a
|
|
38
|
+
# hook packed +depth+ directories below the package root (the depth of
|
|
39
|
+
# the packed sitelibdir plus the two of FEATURE's own directories).
|
|
40
|
+
def self.source(env, depth)
|
|
41
|
+
root_rel = ([".."] * depth).join("/")
|
|
42
|
+
entries = env.map { |name, value|
|
|
43
|
+
" #{name.to_s.dump} => #{value.to_s.dump},"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<<~RUBY
|
|
47
|
+
# frozen_string_literal: true
|
|
48
|
+
# Generated by OCRAN. Gives a bare bin/ruby of this package the
|
|
49
|
+
# environment of the launch script: each variable below that is not
|
|
50
|
+
# set yet is set, with "#{EXTRACT_ROOT}/" standing for the package root,
|
|
51
|
+
# and each one the launch script empties is removed, as it does,
|
|
52
|
+
# and then the next #{FEATURE} on the load path (the one the Ruby
|
|
53
|
+
# installation ships, if any) is loaded, as RubyGems would have done.
|
|
54
|
+
# RUBYOPT, LD_LIBRARY_PATH and DYLD_LIBRARY_PATH take effect for child
|
|
55
|
+
# processes only: they are read before this file runs.
|
|
56
|
+
ocran_env = {
|
|
57
|
+
#{entries.join("\n")}
|
|
58
|
+
}
|
|
59
|
+
ocran_root = File.expand_path(#{root_rel.dump}, __dir__)
|
|
60
|
+
ocran_windows = File::ALT_SEPARATOR == "\\\\"
|
|
61
|
+
ocran_same = lambda do |entry, dir|
|
|
62
|
+
entry = File.expand_path(entry)
|
|
63
|
+
ocran_windows ? entry.casecmp?(dir) : entry == dir
|
|
64
|
+
end
|
|
65
|
+
ocran_feature = lambda { |dir| File.join(File.expand_path(dir), #{FEATURE.dump}) }
|
|
66
|
+
ocran_this = lambda { |file| File.file?(file) && File.identical?(file, __FILE__) }
|
|
67
|
+
# RUBYLIB goes where Ruby would have put it: just before the
|
|
68
|
+
# directory this file was found in, the first default entry.
|
|
69
|
+
ocran_index = $LOAD_PATH.index { |dir| ocran_this.call(ocran_feature.call(dir)) } || 0
|
|
70
|
+
ocran_gem_paths = false
|
|
71
|
+
ocran_env.each do |name, value|
|
|
72
|
+
# Removed by the launch script, whatever the parent set: an
|
|
73
|
+
# inherited BUNDLE_GEMFILE must not replace the packaged Gemfile.
|
|
74
|
+
if value.empty?
|
|
75
|
+
ENV.delete(name)
|
|
76
|
+
next
|
|
77
|
+
end
|
|
78
|
+
next if ENV.key?(name)
|
|
79
|
+
|
|
80
|
+
value = value.gsub(%r{#{Regexp.escape(EXTRACT_ROOT.to_s)}[/\\\\]}) { "\#{ocran_root}/" }
|
|
81
|
+
value = value.tr("/", "\\\\") if ocran_windows
|
|
82
|
+
ENV[name] = value
|
|
83
|
+
case name
|
|
84
|
+
when "GEM_HOME", "GEM_PATH"
|
|
85
|
+
ocran_gem_paths = true
|
|
86
|
+
when "RUBYLIB"
|
|
87
|
+
dirs = value.split(File::PATH_SEPARATOR).reject(&:empty?).map { |dir| File.expand_path(dir) }
|
|
88
|
+
dirs.reject! { |dir| $LOAD_PATH.any? { |entry| ocran_same.call(entry, dir) } }
|
|
89
|
+
$LOAD_PATH.insert(ocran_index, *dirs.uniq)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
Gem.clear_paths if ocran_gem_paths && defined?(Gem.clear_paths)
|
|
93
|
+
ocran_original = $LOAD_PATH.map(&ocran_feature).find { |file| File.file?(file) && !ocran_this.call(file) }
|
|
94
|
+
require ocran_original if ocran_original
|
|
95
|
+
RUBY
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -90,8 +90,12 @@ module Ocran
|
|
|
90
90
|
# Skip if found in known Gem dir
|
|
91
91
|
elsif (spec = GemSpecQueryable.find_spec(feature))
|
|
92
92
|
gems << spec
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
elsif verbose && Gem.path.any? { |gem_path| feature.subpath?(File.join(gem_path, "gems")) }
|
|
94
|
+
# Only a feature in a gem directory is expected to have a
|
|
95
|
+
# gemspec. Standard library and application files are not gems,
|
|
96
|
+
# and reporting each of them buried real errors under hundreds
|
|
97
|
+
# of lines.
|
|
98
|
+
puts "Failed to load gemspec for #{feature}"
|
|
95
99
|
end
|
|
96
100
|
|
|
97
101
|
gems
|
data/lib/ocran/option.rb
CHANGED
|
@@ -886,5 +886,45 @@ EOF
|
|
|
886
886
|
return false unless Gem.win_platform?
|
|
887
887
|
@options[:windowed?]
|
|
888
888
|
end
|
|
889
|
+
|
|
890
|
+
# Where the compiled launcher stubs live (StubBuilder::STUB_PATH and
|
|
891
|
+
# STUBW_PATH). A released gem ships them; a git checkout, such as
|
|
892
|
+
# Bundler's `gem "ocran", git: ...`, has only their sources in src/.
|
|
893
|
+
STUB_DIR = File.expand_path("../../share/ocran", __dir__)
|
|
894
|
+
|
|
895
|
+
# The prebuilt stub the build will copy, or nil when it copies none:
|
|
896
|
+
# native compilation, --wasm, --static-runtime and --cosmo-ruby's ZIP
|
|
897
|
+
# mode use no stub, --cosmo compiles its own, and installer, directory
|
|
898
|
+
# and zip output only need one for the wrapper executable.
|
|
899
|
+
def prebuilt_stub
|
|
900
|
+
return nil if spinel? || roundhouse? || wasm_runtime || static_runtime? || cosmo_zip? || cosmo_cc
|
|
901
|
+
return nil if (inno_setup_script || output_dir || output_zip) && !wrapper_exe?
|
|
902
|
+
|
|
903
|
+
name = if !Gem.win_platform? then "stub"
|
|
904
|
+
elsif windowed? && !macosx_bundle then "stubw.exe"
|
|
905
|
+
else "stub.exe"
|
|
906
|
+
end
|
|
907
|
+
File.join(STUB_DIR, name)
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
# Fails when the build needs a stub that was never compiled. Checked
|
|
911
|
+
# before the dependency run, which can take minutes, since the build
|
|
912
|
+
# would otherwise end in a bare ENOENT for the stub after it.
|
|
913
|
+
def check_prebuilt_stub(path = prebuilt_stub)
|
|
914
|
+
return if path.nil? || File.exist?(path)
|
|
915
|
+
|
|
916
|
+
root = File.dirname(STUB_DIR, 2)
|
|
917
|
+
build = if RUBY_PLATFORM.include?("mswin")
|
|
918
|
+
"`nmake -f Makefile.mswin install` in #{File.join(root, "src")} from a Visual Studio developer prompt"
|
|
919
|
+
elsif Gem.win_platform?
|
|
920
|
+
"`ridk exec make -C src install` in #{root}"
|
|
921
|
+
else
|
|
922
|
+
"`make -C src install` in #{root}"
|
|
923
|
+
end
|
|
924
|
+
message = "The launcher stub #{path} has not been built (OCRAN from a git checkout " \
|
|
925
|
+
"ships only its sources): run #{build}"
|
|
926
|
+
message += ", or pass --no-wrapper-exe to skip the wrapper executable" if inno_setup_script || output_dir || output_zip
|
|
927
|
+
raise message
|
|
928
|
+
end
|
|
889
929
|
end
|
|
890
930
|
end
|
data/lib/ocran/runner.rb
CHANGED
|
@@ -29,6 +29,7 @@ module Ocran
|
|
|
29
29
|
load File.expand_path("option.rb", __dir__)
|
|
30
30
|
@option = Option.new.tap do |opt|
|
|
31
31
|
opt.parse(ARGV)
|
|
32
|
+
opt.check_prebuilt_stub
|
|
32
33
|
rescue *USER_ERRORS => e
|
|
33
34
|
# Invalid option arguments, usually; report them as such.
|
|
34
35
|
user_error e
|
|
@@ -207,6 +208,14 @@ module Ocran
|
|
|
207
208
|
# environment, so it is preferable to ignore exceptions other than
|
|
208
209
|
# critical errors.
|
|
209
210
|
warning "#{mod}::#{const} loading failed: #{e.message}"
|
|
211
|
+
rescue SystemExit => e
|
|
212
|
+
# Some libraries exit when an optional dependency is missing -
|
|
213
|
+
# rdoc/servlet.rb aborts without webrick, which Ruby 3.4 no
|
|
214
|
+
# longer ships. That ended the whole build (exit 1) over a
|
|
215
|
+
# constant the application never asked for; the abort message
|
|
216
|
+
# went to stderr ahead of the buffered progress output.
|
|
217
|
+
warning "#{mod}::#{const} loading exited (status #{e.status})" \
|
|
218
|
+
"#{e.message == "exit" ? "" : ": #{e.message}"}"
|
|
210
219
|
end
|
|
211
220
|
end
|
|
212
221
|
checked_modules[mod] = true
|
data/lib/ocran/runtime_sdk.rb
CHANGED
|
@@ -188,6 +188,10 @@ module Ocran
|
|
|
188
188
|
cmd = cmd.map { |a| as_dll.fetch(a, a) }
|
|
189
189
|
cmd += ["-o", output.to_s] unless args.any? { |a| a.include?("@OUTPUT@") }
|
|
190
190
|
cmd += ["-Wl,-sectcreate,__OCRAN,__zip,#{archive}"] if archive
|
|
191
|
+
# macOS: no debug map (the objects' paths, here in a temporary
|
|
192
|
+
# directory, would differ every time), and nothing for strip to do,
|
|
193
|
+
# which would break the signature the linker writes.
|
|
194
|
+
cmd += ["-Wl,-S"] if @info["target_os"].to_s.start_with?("darwin")
|
|
191
195
|
run(*cmd)
|
|
192
196
|
end
|
|
193
197
|
strip = strip_tool
|
|
@@ -242,8 +246,11 @@ module Ocran
|
|
|
242
246
|
# Compilers, linkers and strip run with SOURCE_DATE_EPOCH set: binutils
|
|
243
247
|
# then writes that, not the current time, into a PE header it rewrites
|
|
244
248
|
# (strip does), so that the same application links to the same bytes.
|
|
249
|
+
# ZERO_AR_DATE does the same for Apple's ld, which otherwise writes the
|
|
250
|
+
# objects' modification times into the executable's debug map.
|
|
245
251
|
def run(*cmd)
|
|
246
|
-
|
|
252
|
+
env = { "SOURCE_DATE_EPOCH" => ENV["SOURCE_DATE_EPOCH"] || "1767225600", "ZERO_AR_DATE" => "1" }
|
|
253
|
+
out, status = Open3.capture2e(env, *cmd)
|
|
247
254
|
raise "#{cmd.first} failed:\n#{out.lines.last(25).join}" unless status.success?
|
|
248
255
|
|
|
249
256
|
out
|
data/lib/ocran/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocran
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.5.
|
|
4
|
+
version: 1.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andi Idogawa
|
|
@@ -59,6 +59,7 @@ files:
|
|
|
59
59
|
- lib/ocran/ed_resource.rb
|
|
60
60
|
- lib/ocran/ed_resource_builder.rb
|
|
61
61
|
- lib/ocran/empty_source
|
|
62
|
+
- lib/ocran/env_hook.rb
|
|
62
63
|
- lib/ocran/file_path_set.rb
|
|
63
64
|
- lib/ocran/gem_spec_queryable.rb
|
|
64
65
|
- lib/ocran/host_config_helper.rb
|
|
@@ -120,6 +121,8 @@ metadata:
|
|
|
120
121
|
homepage_uri: https://github.com/largo/ocran
|
|
121
122
|
source_code_uri: https://github.com/largo/ocran
|
|
122
123
|
changelog_uri: https://github.com/largo/ocran/blob/master/CHANGELOG.txt
|
|
124
|
+
bug_tracker_uri: https://github.com/largo/ocran/issues
|
|
125
|
+
documentation_uri: https://github.com/largo/ocran#readme
|
|
123
126
|
rdoc_options: []
|
|
124
127
|
require_paths:
|
|
125
128
|
- lib
|