diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe64d98..e54a181 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,20 +5,23 @@ on: [push] jobs: build: strategy: + fail-fast: false matrix: os: [ubuntu-latest] - lua_version: [5.1, 5.2, 5.3, 5.4, luajit-2.0.5, luajit-2.1.0-beta3] + lua_version: [5.1, 5.2, 5.3, 5.4, luajit-2.0, luajit-2.1] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 + with: + persist-credentials: false - - uses: leafo/gh-actions-lua@v8.0.0 + - uses: leafo/gh-actions-lua@v13 with: luaVersion: ${{ matrix.lua_version }} - - uses: leafo/gh-actions-luarocks@v4.0.0 + - uses: leafo/gh-actions-luarocks@v6 - name: Setup rocks depended run: | @@ -29,5 +32,35 @@ jobs: - name: Build run: luarocks make + - name: Check the built module is portable + shell: bash + run: | + set -euo pipefail + # Anything above the x86-64 baseline (SSE2) makes the module die with + # SIGILL on older CPUs, and nothing else catches that until someone + # loads it there. The recorded compile flags are the exact check; the + # disassembly is the backstop for instructions that arrive by other + # means, such as intrinsics behind RAPIDJSON_SSE42. + flags=$(grep -h '^CXX_FLAGS' build.luarocks/CMakeFiles/*/flags.make) + if grep -qE -- '-m(sse[34]|ssse3|avx|fma|bmi|popcnt|aes|pclmul|f16c|abm|lzcnt|movbe|adx|sha)' <<<"$flags" || + grep -oE -- '-march=[^[:space:]]+' <<<"$flags" | grep -qvE -- '-march=x86-64(-v1)?$'; then + echo "$flags" + echo "the module is built for a specific CPU, not the x86-64 baseline" >&2 + exit 1 + fi + + module=build.luarocks/rapidjson.so + test -f "$module" + beyond=$(objdump -d --no-show-raw-insn "$module" \ + | grep -oE '^[[:space:]]+[0-9a-f]+:[[:space:]]+[a-z][a-z0-9]*' \ + | awk '{print $2}' | sort -u \ + | grep -E '^(v[a-z][a-z0-9]*|k[a-z][a-z0-9]*|popcnt|lzcnt|tzcnt|crc32|movbe|adcx|adox|prefetchw|andn|bextr|bls[imr]|bzhi|mulx|pdep|pext|rorx|sarx|shlx|shrx|aes[a-z]*|pclmul[a-z]*|sha1[a-z]*|sha256[a-z]*|pcmp[ei]str[im]|pcmpgtq|ptest|pblendw|pblendvb|blendp[sd]|blendvp[sd]|dpp[sd]|pmov[sz]x[a-z]*|pmulld|pmuldq|packusdw|phminposuw|mpsadbw|movntdqa|round[sp][sd]|insertps|extractps|phadd[a-z]*|phsub[a-z]*|pmaddubsw|pmulhrsw|pabs[bwd]|palignr|pshufb|psign[bwd]|lddqu|movddup|movs[hl]dup|addsubp[sd]|haddp[sd]|hsubp[sd]|pm(in|ax)(s[bd]|u[dw])|rdrand|rdseed|rdpid|xbegin|xend|xabort|xtest|rd[fg]sbase|wr[fg]sbase|tile[a-z]*|ldtilecfg|sttilecfg|clwb|clflushopt|movdir[a-z0-9]*|serialize)$' || true) + if [ -n "$beyond" ]; then + printf '%s\n' "$beyond" | awk 'NR <= 6' | tr '\n' ' ' + echo + echo "rapidjson.so uses instructions above the x86-64 baseline" >&2 + exit 1 + fi + - name: Test run: busted diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c4c82a7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Release + +on: + push: + branches: + - "master" + paths: + - 'rockspec/**' + +permissions: + contents: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install Lua + uses: leafo/gh-actions-lua@v13 + + - name: Install Luarocks + uses: leafo/gh-actions-luarocks@v6 + + - name: Extract release name + id: release_env + shell: bash + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + run: | + title="${COMMIT_MESSAGE}" + # tolerate the " (#N)" a squash merge appends to the title + re="^feat: release v([^[:space:]]+)" + if [[ $title =~ $re ]]; then + echo "version=v${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + echo "version_without_v=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + else + echo "commit message must be 'feat: release v'" + exit 1 + fi + + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.release_env.outputs.version }} + run: | + # keep this retryable: a failed upload below is re-run with the + # release already created + if gh release view "${VERSION}" >/dev/null 2>&1; then + echo "release ${VERSION} already exists" + else + gh release create "${VERSION}" --title "${VERSION}" --generate-notes + fi + + - name: Upload to luarocks + env: + LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }} + VERSION: ${{ steps.release_env.outputs.version_without_v }} + run: | + luarocks install dkjson + luarocks upload "rockspec/api7-lua-rapidjson-${VERSION}-0.rockspec" --api-key="${LUAROCKS_TOKEN}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 117c69d..8875ac5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,10 +32,17 @@ if(UNIX) set(LINK_FLAGS "-shared") endif(APPLE) add_compile_options(-g -Wall -fPIC) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_ARCH_NATIVE) - if (COMPILER_SUPPORTS_ARCH_NATIVE) - add_compile_options(-march=native) + # -march=native bakes the build host's CPU features into rapidjson.so, so a + # module built on a modern machine dies with SIGILL on an older one. Keep it + # opt-in: builds that get distributed (packages, images, luarocks) must stay + # portable. + option(LUA_RAPIDJSON_ARCH_NATIVE "Optimize for the build host CPU; produces a non-portable binary" OFF) + if (LUA_RAPIDJSON_ARCH_NATIVE) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_ARCH_NATIVE) + if (COMPILER_SUPPORTS_ARCH_NATIVE) + add_compile_options(-march=native) + endif() endif() else(UNIX) if(WIN32)