Skip to content

Commit a713ff7

Browse files
authored
Build statically linked Linux binaries in CI (rescript-lang#5872)
* Build statically linked Linux executables in a container * PR review feedback
1 parent f3c8562 commit a713ff7

10 files changed

+248
-101
lines changed

.github/workflows/ci.yml

+77-51
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,53 @@ concurrency:
1111
# Cancel previous builds for pull requests only.
1212
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
1313

14+
env:
15+
OCAMLRUNPARAM: b
16+
DUNE_PROFILE: release
17+
1418
jobs:
19+
# Build statically linked Linux binaries in an Alpine-based Docker container
20+
# See https://ocamlpro.com/blog/2021_09_02_generating_static_and_portable_executables_with_ocaml
21+
# for more info.
22+
# The container already comes with all required tools pre-installed
23+
# (see https://github.com/rescript-lang/docker-rescript-ci-build/blob/main/Dockerfile).
24+
static-binaries-linux:
25+
runs-on: ubuntu-latest
26+
27+
container:
28+
image: ghcr.io/rescript-lang/rescript-ci-build
29+
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v3
33+
34+
- name: Apply static linking patch
35+
run: git apply scripts/dune-static-linking.patch
36+
37+
- name: Build compiler binaries
38+
run: opam exec -- dune build
39+
40+
- name: Build ninja binary
41+
working-directory: ninja
42+
env:
43+
LDFLAGS: -static
44+
run: python3 configure.py --bootstrap --verbose
45+
46+
- name: "Upload artifacts"
47+
uses: actions/upload-artifact@v3
48+
with:
49+
name: static-binaries-linux
50+
path: |
51+
_build/install/default/bin
52+
ninja/ninja
53+
1554
build:
55+
needs: static-binaries-linux
56+
1657
strategy:
1758
fail-fast: false
1859
matrix:
19-
# Stay on the oldest Ubuntu version that's still supported by Github Actions
20-
# to avoid glibc incompatibilities as far as possible.
21-
os: [macos-latest, ubuntu-20.04, windows-latest, macos-arm]
60+
os: [macos-latest, ubuntu-latest, windows-latest, macos-arm]
2261
ocaml_compiler: [4.14.0]
2362

2463
runs-on: ${{matrix.os}}
@@ -39,6 +78,18 @@ jobs:
3978
with:
4079
fetch-depth: 2 # to be able to check for changes in subfolder res_syntax later
4180

81+
- name: Download static linux binaries
82+
if: runner.os == 'Linux'
83+
uses: actions/download-artifact@v3
84+
with:
85+
name: static-binaries-linux
86+
87+
- name: Make static linux binaries executable
88+
if: runner.os == 'Linux'
89+
run: |
90+
chmod +x ninja/ninja
91+
chmod +x _build/install/default/bin/*
92+
4293
- name: Use OCaml ${{matrix.ocaml_compiler}}
4394
uses: ocaml/setup-ocaml@v2
4495
with:
@@ -49,19 +100,31 @@ jobs:
49100
- name: "Install OPAM dependencies"
50101
run: opam install . --deps-only
51102

103+
- name: "Build compiler"
104+
if: runner.os != 'Linux'
105+
run: opam exec -- dune build
106+
52107
- name: Use Node.js
53108
uses: actions/setup-node@v3
54109
with:
55110
node-version: 16
56111

57-
- name: "Build compiler"
58-
run: |
59-
opam exec -- dune build
60-
node ./scripts/copyExes.js
61-
62112
- name: Install npm packages
63113
run: npm ci --ignore-scripts
64114

115+
- name: "Windows: Use MSVC for ninja build"
116+
if: runner.os == 'Windows'
117+
uses: TheMrMilchmann/setup-msvc-dev@v2
118+
with:
119+
arch: x64
120+
121+
- name: Build ninja
122+
if: runner.os != 'Linux'
123+
run: node scripts/buildNinjaBinary.js
124+
125+
- name: Copy exes to platform bin dirs
126+
run: node ./scripts/copyExes.js
127+
65128
- name: "Check if syntax subfolder has changed"
66129
id: syntax-diff
67130
shell: bash
@@ -74,38 +137,28 @@ jobs:
74137
75138
- name: "Syntax: Run roundtrip tests"
76139
if: ${{ env.syntax_status == 'changed' && runner.os != 'Windows' }}
77-
run: opam exec -- make test-syntax-roundtrip
140+
run: make test-syntax-roundtrip
78141

79142
- name: "Syntax: Run tests (Windows)"
80143
if: ${{ env.syntax_status == 'changed' && runner.os == 'Windows' }}
81-
run: opam exec -- make test-syntax
82-
83-
# Required for ninja build
84-
- name: "Windows: Use MSVC"
85-
if: runner.os == 'Windows'
86-
uses: TheMrMilchmann/setup-msvc-dev@v2
87-
with:
88-
arch: x64
89-
90-
- name: Build ninja
91-
run: node scripts/buildNinjaBinary.js
144+
run: make test-syntax
92145

93146
- name: Build runtime/stdlib
94147
if: runner.os != 'Windows'
95148
run: |
96-
opam exec -- dune exec -- node ./scripts/ninja.js config
97-
opam exec -- dune exec -- node ./scripts/ninja.js build
149+
opam exec -- node ./scripts/ninja.js config
150+
opam exec -- node ./scripts/ninja.js build
98151
99152
- name: Check for changes in lib folder
100153
run: git diff --exit-code lib/js lib/es6
101154

102155
- name: Run tests
103156
if: runner.os != 'Windows'
104-
run: opam exec -- dune exec -- node scripts/ciTest.js -all
157+
run: node scripts/ciTest.js -all
105158

106159
- name: Run tests (Windows)
107160
if: runner.os == 'Windows'
108-
run: opam exec -- dune exec -- node scripts/ciTest.js -mocha -theme -format
161+
run: node scripts/ciTest.js -mocha -theme -format
109162

110163
- name: Prepare artifact upload
111164
run: |
@@ -216,30 +269,3 @@ jobs:
216269
run: npx rescript -h && npx rescript build && cat src/Test.bs.js
217270
shell: bash
218271
working-directory: packages/test
219-
220-
# The following job tests installation from source on FreeBSD via cross-platform-actions.
221-
# Disabled by default, as it takes ~11m.
222-
# Can be enabled temporarily in a PR to verify that installation from source
223-
# on an "exotic" platform still works.
224-
#
225-
# installationTestFreeBSD:
226-
# needs: package
227-
# runs-on: macos-latest
228-
#
229-
# steps:
230-
# - name: Checkout
231-
# uses: actions/checkout@v3
232-
#
233-
# - name: Download artifacts
234-
# uses: actions/download-artifact@v3
235-
# with:
236-
# name: npm-packages
237-
# path: packages/test
238-
#
239-
# - name: Test installation on FreeBSD
240-
# uses: cross-platform-actions/action@v0.6.2
241-
# with:
242-
# operating_system: freebsd
243-
# version: "13.0"
244-
# shell: bash
245-
# run: cd packages/test && ./test_freebsd.sh

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ _build
3636
*.rawlambda
3737
*.lambda
3838
*.zip
39-
*.patch
4039
*.mlast
4140
*.mliast
4241
# special file

Makefile

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
SHELL = /bin/bash
22

3+
DUNE_BIN_DIR = ./_build/install/default/bin
4+
35
build:
46
dune build
57
./scripts/copyExes.js
@@ -8,42 +10,40 @@ watch:
810
dune build -w
911

1012
bench:
11-
dune exec -- syntax_benchmarks
13+
$(DUNE_BIN_DIR)/syntax_benchmarks
1214

1315
dce:
14-
opam exec reanalyze.exe -- -dce-cmt _build
16+
reanalyze.exe -- -dce-cmt _build
1517

1618
ninja/ninja:
1719
./scripts/buildNinjaBinary.js
1820

1921
ninja: ninja/ninja
2022

2123
test: lib
22-
dune exec -- node scripts/ciTest.js -all
24+
node scripts/ciTest.js -all
2325

24-
test-syntax: build
25-
dune exec -- syntax_tests
26-
dune exec -- bash ./scripts/test_syntax.sh
26+
test-syntax:
27+
bash ./scripts/test_syntax.sh
2728
make reanalyze
2829
bash ./scripts/testok.sh
2930

30-
test-syntax-roundtrip: build
31-
dune exec -- syntax_tests
32-
ROUNDTRIP_TEST=1 dune exec -- bash ./scripts/test.sh
31+
test-syntax-roundtrip:
32+
ROUNDTRIP_TEST=1 bash ./scripts/test.sh
3333
make reanalyze
3434
bash ./scripts/testok.sh
3535

36-
test-gentype: build
36+
test-gentype:
3737
make -C jscomp/gentype_tests/typescript-react-example clean test
3838

3939
test-all: test test-gentype
4040

4141
reanalyze:
4242
reanalyze.exe -set-exit-code -all-cmt _build/default/res_syntax -suppress res_syntax/testrunner
4343

44-
lib: build ninja/ninja
45-
dune exec -- node scripts/ninja.js config
46-
dune exec -- node scripts/ninja.js build
44+
lib: ninja/ninja
45+
node scripts/ninja.js config
46+
node scripts/ninja.js build
4747

4848
artifacts: lib
4949
./scripts/prebuilt.js

scripts/buildNinjaBinary.js

-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
#!/usr/bin/env node
22

33
const child_process = require("child_process");
4-
const fs = require("fs");
54
const path = require("path");
65

7-
const { absolutePath: platformBinDir, ninja_exe } = require("./bin_path");
8-
96
const platform = process.platform;
107
const ninjaDir = path.join(__dirname, "..", "ninja");
118
const buildCommand = "python3 configure.py --bootstrap --verbose";
@@ -20,11 +17,3 @@ if (platform === "win32") {
2017
child_process.execSync(buildCommand, { stdio: [0, 1, 2], cwd: ninjaDir });
2118
child_process.execSync(`strip ninja`, { stdio: [0, 1, 2], cwd: ninjaDir });
2219
}
23-
24-
// Copy exe to platform bin dir
25-
if (!fs.existsSync(platformBinDir)) {
26-
fs.mkdirSync(platformBinDir);
27-
}
28-
29-
const ext = process.platform === "win32" ? ".exe" : "";
30-
fs.copyFileSync(path.join(ninjaDir, "ninja" + ext), ninja_exe);

scripts/ciTest.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ var cp = require("child_process");
33
var path = require("path");
44
var fs = require("fs");
55

6+
var duneBinDir = require("./dune").duneBinDir;
7+
68
var ounitTest = false;
79
var mochaTest = false;
810
var themeTest = false;
@@ -43,7 +45,7 @@ if (all) {
4345

4446
function runTests() {
4547
if (ounitTest) {
46-
cp.execSync("ounit_tests", {
48+
cp.execSync(path.join(duneBinDir, "ounit_tests"), {
4749
stdio: [0, 1, 2],
4850
});
4951
}

scripts/copyExes.js

+9-14
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,18 @@
44
const path = require("path");
55
const fs = require("fs");
66
const child_process = require("child_process");
7+
const { duneBinDir } = require("./dune");
8+
const { absolutePath: platformBinDir } = require("./bin_path");
79

8-
const platformBinDir = require("./bin_path").absolutePath;
9-
const duneBinDir = path.join(
10-
__dirname,
11-
"..",
12-
"_build",
13-
"install",
14-
"default",
15-
"bin"
16-
);
10+
const ninjaDir = path.join(__dirname, "..", "ninja");
1711

1812
if (!fs.existsSync(platformBinDir)) {
1913
fs.mkdirSync(platformBinDir);
2014
}
2115

22-
function copyExe(exe) {
16+
function copyExe(dir, exe) {
2317
const ext = process.platform === "win32" ? ".exe" : "";
24-
const src = path.join(duneBinDir, exe + ext);
18+
const src = path.join(dir, exe + ext);
2519
const dest = path.join(platformBinDir, exe + ".exe");
2620

2721
// For some reason, the copy operation fails in Windows CI if the file already exists.
@@ -36,6 +30,7 @@ function copyExe(exe) {
3630
}
3731
}
3832

39-
copyExe("rescript");
40-
copyExe("bsc");
41-
copyExe("bsb_helper");
33+
copyExe(duneBinDir, "rescript");
34+
copyExe(duneBinDir, "bsc");
35+
copyExe(duneBinDir, "bsb_helper");
36+
copyExe(ninjaDir, "ninja");

0 commit comments

Comments
 (0)