Skip to content

Commit f7919ba

Browse files
committed
Fix native image build issues
- Register conf/native-image as a nativeCompile input, so editing the reflection config actually triggers a rebuild instead of reporting UP-TO-DATE and shipping the previous binary. - Drop the graalvmNative agent block. It only takes effect with -Pagent, and generateNativeImageConfig does the tracing instead. - Make the simulator send workspace/didChangeConfiguration. This is what initializes the language services, so without it every language feature returned an empty result and the tracing agent observed almost nothing but lsp4j plumbing. The resulting binary answered "initialize" but was dead for real use: it could not deserialize DidChangeConfigurationParams or ExecuteCommandParams, and reported false errors such as "Unrecognized process input qualifier val". - Also send workspace/executeCommand, use a real temp workspace instead of hardcoded /Users paths, and wait for outstanding async requests before shutdown -- responses arrive out of order and "exit" was truncating them. - Strengthen the binary test. Checking only for an "initialize" response passes on a binary whose language features are all broken; also check for missing-metadata errors and for a non-empty documentSymbol result. - Fix the release job: it collected *.tar.gz while the build uploaded a bare binary, and read the version from refs/tags while the workflow ignored tags. Package the binary as a tarball and trigger the release on v* tags. - Limit push builds to main and tags, so a PR no longer builds four native images twice. Add timeout-minutes, and use the free macos-latest runner for Apple Silicon instead of the billed macos-latest-xlarge. - Remove duplicatesStrategy from the jar task and the redundant copy it was masking; processResources already puts definitions.json in spec/. - Rename the binary from nlsp to nextflow-lsp.
1 parent 02b4ebd commit f7919ba

4 files changed

Lines changed: 149 additions & 83 deletions

File tree

.github/workflows/build-native.yml

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,22 @@ name: Build Native Image
33
on:
44
push:
55
branches:
6-
- '*'
7-
- '!refs/tags/.*'
8-
tags-ignore:
9-
- '*'
6+
- 'main'
7+
tags:
8+
- 'v*'
109
pull_request:
1110
types: [opened, reopened, synchronize]
12-
branches:
13-
- '*'
14-
- '!refs/tags/.*'
15-
tags-ignore:
16-
- '*'
11+
workflow_dispatch:
1712

1813
env:
19-
GRAALVM_VERSION: '21'
2014
GRAALVM_DISTRIBUTION: 'graalvm'
2115
JAVA_VERSION: '21'
2216

2317
jobs:
2418
build:
2519
name: Build ${{ matrix.platform }}
2620
runs-on: ${{ matrix.os }}
27-
if: "!contains(github.event.head_commit.message, '[skip ci]')"
21+
timeout-minutes: 60
2822
strategy:
2923
fail-fast: false
3024
matrix:
@@ -33,9 +27,10 @@ jobs:
3327
platform: linux-amd64
3428
- os: ubuntu-24.04-arm
3529
platform: linux-arm64
30+
# NOTE: there is no free Intel macOS runner; this one is billed per minute
3631
- os: macos-latest-large
3732
platform: macos-intel
38-
- os: macos-latest-xlarge
33+
- os: macos-latest
3934
platform: macos-silicon
4035

4136
steps:
@@ -57,25 +52,27 @@ jobs:
5752
chmod +x ./gradlew ./lsp-simulator.sh ./build-native.sh
5853
./build-native.sh
5954
55+
- name: Package binary
56+
run: |
57+
tar -czf nextflow-lsp-${{ matrix.platform }}.tar.gz \
58+
-C build/native/nativeCompile nextflow-lsp
59+
6060
- name: Upload artifact
6161
uses: actions/upload-artifact@v4
6262
with:
63-
name: nf-language-server-${{ matrix.platform }}
64-
path: build/native/nativeCompile/nlsp
63+
name: nextflow-lsp-${{ matrix.platform }}
64+
path: nextflow-lsp-${{ matrix.platform }}.tar.gz
6565
retention-days: 7
6666

6767
release:
6868
name: Create Release
6969
needs: build
7070
runs-on: ubuntu-latest
71-
if: contains(github.event.head_commit.message, '[release]')
71+
if: startsWith(github.ref, 'refs/tags/v')
7272
permissions:
7373
contents: write
7474

7575
steps:
76-
- name: Checkout repository
77-
uses: actions/checkout@v4
78-
7976
- name: Download all artifacts
8077
uses: actions/download-artifact@v4
8178
with:
@@ -84,21 +81,17 @@ jobs:
8481
- name: Prepare release assets
8582
run: |
8683
mkdir -p release
87-
find artifacts -name "*.tar.gz" -exec cp {} release/ \;
84+
find artifacts -name '*.tar.gz' -exec cp {} release/ \;
8885
cd release
8986
sha256sum *.tar.gz > checksums.txt
9087
cat checksums.txt
9188
92-
- name: Extract version from tag
93-
id: version
94-
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
95-
9689
- name: Create GitHub Release
9790
uses: softprops/action-gh-release@v2
9891
with:
99-
name: Release ${{ steps.version.outputs.VERSION }}
92+
name: Release ${{ github.ref_name }}
10093
draft: false
101-
prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') }}
94+
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') }}
10295
generate_release_notes: true
10396
files: |
10497
release/*.tar.gz

build-native.sh

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ build_native_image() {
105105
log_info "Building native image (this includes JAR build and tracing agent)..."
106106
./gradlew nativeCompile --no-configuration-cache --no-daemon
107107

108-
local BINARY_PATH="build/native/nativeCompile/nlsp${BINARY_EXT}"
108+
local BINARY_PATH="build/native/nativeCompile/nextflow-lsp${BINARY_EXT}"
109109
if [[ -f "$BINARY_PATH" ]]; then
110110
log_info "Native image built successfully: $BINARY_PATH"
111111
ls -lh "$BINARY_PATH"
@@ -119,18 +119,41 @@ build_native_image() {
119119
test_native_binary() {
120120
log_info "Testing native binary..."
121121

122-
local BINARY_PATH="build/native/nativeCompile/nlsp${BINARY_EXT}"
122+
local BINARY_PATH="build/native/nativeCompile/nextflow-lsp${BINARY_EXT}"
123123
local OUTPUT
124124

125-
OUTPUT=$(./lsp-simulator.sh | "$BINARY_PATH" 2>&1 | head -20)
125+
OUTPUT=$(./lsp-simulator.sh | "$BINARY_PATH" 2>&1) || true
126126

127-
if echo "$OUTPUT" | grep -q '"id":1,"result"'; then
128-
log_info "Native binary test passed - LSP initialize succeeded"
129-
else
127+
# A binary missing reflection metadata still answers `initialize` -- it
128+
# fails later, when lsp4j tries to deserialize a request it was never
129+
# traced with. So check for those failures, and for a request that only
130+
# succeeds once the language services are actually running.
131+
local FAILED=false
132+
133+
if ! echo "$OUTPUT" | grep -q '"id":1,"result"'; then
134+
log_error "LSP initialize did not succeed"
135+
FAILED=true
136+
fi
137+
138+
if echo "$OUTPUT" | grep -q 'was never registered'; then
139+
log_error "Missing reflection metadata:"
140+
echo "$OUTPUT" | grep 'was never registered' | sort -u
141+
FAILED=true
142+
fi
143+
144+
if ! echo "$OUTPUT" | grep -q '"id":6,"result":\[{'; then
145+
log_error "textDocument/documentSymbol returned no symbols -- the"
146+
log_error "language services are not working in the native binary"
147+
FAILED=true
148+
fi
149+
150+
if [[ "$FAILED" == "true" ]]; then
130151
log_error "Native binary test failed"
131-
echo "$OUTPUT"
152+
echo "$OUTPUT" | head -60
132153
exit 1
133154
fi
155+
156+
log_info "Native binary test passed"
134157
}
135158

136159

@@ -179,7 +202,7 @@ main() {
179202

180203
echo ""
181204
log_info "Build completed successfully!"
182-
log_info "Binary: build/native/nativeCompile/nlsp${BINARY_EXT}"
205+
log_info "Binary: build/native/nativeCompile/nextflow-lsp${BINARY_EXT}"
183206
}
184207

185208
main "$@"

build.gradle

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,6 @@ processResources {
9999

100100
jar {
101101
dependsOn buildSpec
102-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
103-
from("$buildDir/generated") {
104-
include 'definitions.json'
105-
into 'spec'
106-
}
107102
}
108103

109104
application {
@@ -141,30 +136,23 @@ task generateNativeImageConfig(type: Exec) {
141136

142137
tasks.named('nativeCompile') {
143138
dependsOn generateNativeImageConfig
139+
// native-image reads these via -H:ConfigurationFileDirectories, so Gradle
140+
// needs to be told about them or config edits won't trigger a rebuild
141+
inputs.dir("$projectDir/conf/native-image")
144142
}
145143

146144
graalvmNative {
147-
agent {
148-
defaultMode = 'standard'
149-
builtinCallerFilter = true
150-
builtinHeuristicFilter = true
151-
enableExperimentalPredefinedClasses = false
152-
enableExperimentalUnsafeAllocationTracing = false
153-
trackReflectionMetadata = true
154-
metadataCopy {
155-
inputTaskNames.add('run')
156-
outputDirectories.add("$buildDir/native-image-agent")
157-
mergeWithExisting = true
158-
}
159-
}
160145
binaries {
161146
main {
162-
imageName = 'nlsp'
147+
imageName = 'nextflow-lsp'
163148
mainClass = 'nextflow.lsp.NextflowLanguageServer'
164149
buildArgs.addAll([
165150
'--no-fallback',
166151
'--enable-url-protocols=http,https',
167152
'-H:+ReportExceptionStackTraces',
153+
// required: Groovy's indy call sites reach MethodHandleNatives
154+
// methods that native-image cannot compile ahead of time. Deprecated
155+
// upstream, so this will need another answer in a future GraalVM.
168156
'--report-unsupported-elements-at-runtime',
169157
'--initialize-at-build-time=org.slf4j',
170158
'--initialize-at-build-time=org.apache.groovy',

0 commit comments

Comments
 (0)