Compare commits

..
Author SHA1 Message Date
dependabot[bot] a9c983ca47 Bump com.squareup:kotlinpoet from 1.6.0 to 2.4.0
Bumps [com.squareup:kotlinpoet](https://github.com/square/kotlinpoet) from 1.6.0 to 2.4.0.
- [Release notes](https://github.com/square/kotlinpoet/releases)
- [Changelog](https://github.com/square/kotlinpoet/blob/main/docs/changelog.md)
- [Commits](https://github.com/square/kotlinpoet/compare/1.6.0...2.4.0)

---
updated-dependencies:
- dependency-name: com.squareup:kotlinpoet
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-01 18:16:41 +00:00
dependabot[bot] b9dd3c4de1 Bump junit from 6.1.1 to 6.1.3 (#1890)
Bumps `junit` from 6.1.1 to 6.1.3.

Updates `org.junit.jupiter:junit-jupiter-api` from 6.1.1 to 6.1.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.1...r6.1.3)

Updates `org.junit.jupiter:junit-jupiter-engine` from 6.1.1 to 6.1.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.1...r6.1.3)

Updates `org.junit.jupiter:junit-jupiter-params` from 6.1.1 to 6.1.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.1...r6.1.3)

Updates `org.junit.platform:junit-platform-launcher` from 6.1.1 to 6.1.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.1...r6.1.3)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-version: 6.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-version: 6.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.jupiter:junit-jupiter-params
  dependency-version: 6.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.platform:junit-platform-launcher
  dependency-version: 6.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 09:26:31 -07:00
dependabot[bot] d4dac43e30 Bump gradle/actions/dependency-submission from 6.1.0 to 6.3.0 (#1892)
Bumps [gradle/actions/dependency-submission](https://github.com/gradle/actions) from 6.1.0 to 6.3.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/50e97c2cd7a37755bbfafc9c5b7cafaece252f6e...9c971963bec38e04b3d30dcc455b5382be2fdbfb)

---
updated-dependencies:
- dependency-name: gradle/actions/dependency-submission
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 09:05:54 -07:00
dependabot[bot] 15c070621e Bump the first-party group across 1 directory with 2 updates (#1886)
Bumps the first-party group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-java](https://github.com/actions/setup-java).


Updates `actions/checkout` from 6.0.3 to 6.1.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...d23441a48e516b6c34aea4fa41551a30e30af803)

Updates `actions/setup-java` from 5.5.0 to 5.7.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/0f481fcb613427c0f801b606911222b5b6f3083a...b6effb05e454b25005698d916606bdc6ffcbf961)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: first-party
- dependency-name: actions/setup-java
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: first-party
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 08:49:10 -07:00
dependabot[bot] 74c5ded7db Bump slf4j from 2.0.18 to 2.0.20 (#1888)
Bumps `slf4j` from 2.0.18 to 2.0.20.

Updates `org.slf4j:slf4j-api` from 2.0.18 to 2.0.20

Updates `org.slf4j:slf4j-simple` from 2.0.18 to 2.0.20

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-api
  dependency-version: 2.0.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.slf4j:slf4j-simple
  dependency-version: 2.0.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 08:45:52 -07:00
dependabot[bot] a5bcacf91d Bump jline from 4.3.1 to 4.4.6 (#1889)
Bumps `jline` from 4.3.1 to 4.4.6.

Updates `org.jline:jline-reader` from 4.3.1 to 4.4.6
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.3.1...4.4.6)

Updates `org.jline:jline-terminal` from 4.3.1 to 4.4.6
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.3.1...4.4.6)

Updates `org.jline:jline-terminal-jni` from 4.3.1 to 4.4.6
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.3.1...4.4.6)

---
updated-dependencies:
- dependency-name: org.jline:jline-reader
  dependency-version: 4.4.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal
  dependency-version: 4.4.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal-jni
  dependency-version: 4.4.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 08:43:35 -07:00
dependabot[bot] d839bff713 Bump the codeql group with 2 updates (#1891)
Bumps the codeql group with 2 updates: [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/init](https://github.com/github/codeql-action).


Updates `github/codeql-action/analyze` from 4.36.3 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `github/codeql-action/init` from 4.36.3 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: codeql
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: codeql
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 08:36:56 -07:00
Rushikesh 5747506f3a Suppress pkl-gradle POM metadata warning (#1807) 2026-09-30 09:43:59 -07:00
Daniel Chao b8c7a42318 Make typechecking more PE friendly (#1871)
This makes various improvements to help truffle do partial
evaluation of code related to typechecking.

* Split `UnionTypeNode` into `UnionTypeNodeExploded` and
 `UnionTypeNodeLooped` to avoid gigantic graphs. This especially helps
  in the case of typealiases, because they are inlined into their usage
  sites.

* Move constraint-failure exception construction in
  `TypeConstraintNode` behind `@TruffleBoundary` helper methods
  instead of `transferToInterpreterAndInvalidate`, since building the
  exception is a rare, off-hot-path operation.

* Move `ClassNode`'s one-time class-initialization logic behind a
  `@TruffleBoundary` method (`initClass`), store `VmLanguage` as a
  field instead of recomputing it via `VmLanguage.get(this)`, and use
  `transferToInterpreterAndInvalidate` since class init never needs to
  re-run on the compiled path.

* Pass `VmLanguage` explicitly into `TypeTestNode` and `ClassNode`
  (via `AstBuilder`) rather than resolving it with
  `VmLanguage.get(this)` on a hot path.

* Fix various issues where the partial evaluator determines that a frame
  should have been materialized but was not.

* Pre-initialize call targets in `ObjectMember.setMemberNode()` and
  `FunctionLiteralNode` so the first invocation doesn't trigger a
  compilation invalidation.

* Fix truffle node tree issues (don't mark `@RootNode` as child,
  deepCopy instead of passing an already adopted node).

* Remove unnecessary interpreter transfers when throwing type mismatch exceptions
2026-09-30 07:16:54 -07:00
Alexandre Kohler 8331570aaf Fix String.endsWith for overlapping regex matches (#1880) 2026-09-29 21:52:12 -07:00
Jen Basch 1271da2741 Fixes to commands where (sub)commands never extend pkl:Command (#1882) 2026-09-29 23:19:28 +00:00
apple-ospo[bot] e83d8fa14b Replace repo-level CODE_OF_CONDUCT file with the inherited org-wide Code of Conduct in .github repo. (#1878)
Co-authored-by: apple-ospo[bot] <326325757+apple-ospo[bot]@users.noreply.github.com>
2026-09-25 10:51:20 -07:00
Daniel Chao d1e043be4e Make join call overriden toString() (#1872)
This changes the `join` method in `List`/`Set`/`Listing` to
call the overridden `toString()` method.

This is a breaking change, but is considered a bugfix.
2026-09-25 09:24:16 -07:00
Daniel Chao 43e132a383 Fix "unchecked or unsafe operations" warning in javac (#1874)
Fix this printed warning during java compilation:

"Note: Some input files use unchecked or unsafe operations. Recompile with -Xlint:unchecked for details"

* Add `-Xlint:unchecked` as a javac flag
* Fix the various warnings
2026-09-24 09:28:49 -07:00
Daniel Chao 119b1571bb Switch ad-hoc vmobject building to use VmObjectBuilder (#1873) 2026-09-24 07:32:30 -07:00
Daniel Chao d5a7dc021e Switch inferred new {} expressions in method args to using frame slots (#1857)
This changes the logic for inferring `new {}` expressions in method
arguments to using regular frame slots, instead of auxiliary slots.

Overall, this is a performance improvement: no looking up auxiliary slot
numbers, which makes this code much more PE friendly.

* At parse time, get or create a frame slot that holds the method
* Introduce `ArgInfo` record (preserve primitive int) over using `Pair`
* Remove unneeded `isImplicitNewExpr` method; this is not needed because
  we can just keep track of the method slot on the frame descriptor
  builder.
2026-09-22 12:12:57 -07:00
Jen Basch 7ed88e250b Add VmType intermediate representation for types (#1838) 2026-09-22 08:26:03 -07:00
Daniel Chao cbffd6c114 Improve error message when amending a property with no value set (#1867)
Improve error message when amending a property with no value set

This decorates error messages with the amendment node.
2026-09-21 06:54:22 -07:00
Daniel Chao e0b3b87e19 Fix native image build (#1865)
PE code can't call `Object.equals`
2026-09-17 21:57:35 -07:00
Daniel Chao cec08c651e Optimize super property and super subscript nodes (#1853)
This adds cacheing to ReadSuper* nodes so that the same
node being executed can possibly re-use a previously cached value.
2026-09-17 20:46:03 -07:00
Jen Basch 397183a620 Mark overridable external properties as const (#1864) 2026-09-17 12:52:53 -07:00
Jen Basch c71b107e32 Add inference for let bindings (#1824) 2026-09-15 05:20:42 +00:00
Daniel Chao d0de24b85b Avoid using Object.toString() for in-language API (#1850)
This changes code so that the in-language `toString()` doesn't use
Java's `Object.toString()`.
Additionally, the existing `toString()` implementation is switched over
to using `VmValueRenderer`, which does not force (leaves `?` in place of
unforced values).

This helps improve ergonomics when working on the language (can enable
automatically calling `toString()` on debug values).

Additionally, external tools might call toString(); for example,
Truffle's compiler sometimes will stringify node fields (e.g.
when -Dengine.CompilationFailureAction=Throw) is set.
2026-09-11 23:08:26 +00:00
Daniel Chao e3881f6149 Fix invalid prefix when generating spring boot config (#1862)
Fix invalid prefix when generating spring boot config

In Spring Boot, the prefix passed to `@ConfigurationProperties` must
be in "canonical form".

The presence of this annotation allows spring boot to directly inject
properties into a class, but is not actually needed.

The correct behavior here is to omit this annotation if the property
name does not match Spring Boot's canonical form.
2026-09-11 15:49:04 -07:00
Daniel Chao 9b52a4fd82 Fix CI on Windows (#1861)
Fix bad assertion: `IoUtils.getSystemModuleCacheDir()` uses the actual
OS env, but the test hard-codes `isWindows` to false.
2026-09-11 11:09:37 -07:00
Daniel Chao 8fba228790 Fix failing test on Windows (#1858) 2026-09-10 21:31:19 -07:00
Jen Basch 574ad844f3 Use ChoiceFormat for better plurals in error messages (#1848) 2026-09-10 22:03:05 +00:00
Daniel Chao 90245589b6 Improve documentation on netRcHeaders method (#1854) 2026-09-09 15:30:08 -07:00
Yarden Bar c95b3d11f4 Add netrc helpers in EvaluatorSettings.pkl (#1817)
This adds the `netRcHeaders` helper method that parses netrc file contents, and returns a mapping suitable to be used as Http headers.
2026-09-09 14:25:06 -07:00
Daniel Chao 713adac824 Make generator nodes more PE friendly (#1844)
* In GeneratorWhenNode, need to move individual iterations into their
  own methods so that the @ExplodeLoop sees a constant number of
  iterations whenever that method is called.
* In the other nodes, the lambda passed to
  `forceAndIterateMemberValues` needs to capture the materialized frame,
  not the virtual frame.
2026-09-08 15:11:10 -07:00
Dylan Pulver f3efcbfc9b Escape characters that YAML cannot carry literally (#1846)
YamlEmitter only routed a string into YamlEscaper when it held a
character below 0x20, so 0x7F, the C1 block, U+2028 and U+2029 went out
as plain scalars. They are not `c-printable` (YAML 1.2 spec 5.1), and
NEL, LS and PS are line breaks in YAML 1.1, so the rendered document is
either rejected or silently split by a reader.

The escapes added in #1165 for 0x85, 0xA0, U+2028, and U+2029 could only
fire when some other character happened to open that gate. Share one
predicate between the two sites that decide, and give 0x7F-0x9F the
8-bit escape they lack.
2026-09-04 13:46:09 -07:00
Jen Basch f284a0560a Error when inferring a self type in a method argument (#1847) 2026-09-03 23:03:34 +00:00
Jen Basch 249e563291 Optimize new inference in method args (#1845) 2026-09-03 13:16:17 -07:00
Jen Basch 7f5ae2a712 Add inference for implicitly typed new in method arguments (#1768) 2026-09-02 23:55:24 +00:00
Daniel Chao 92b26aca17 Optimize annotations without object bodies (#1843)
This skips some unnecessary steps when evaluating annotations that
have no attached object bodies by returning an EmptyObjectLiteralNode
instead of a PropertiesLiteralNode
2026-09-02 13:04:26 -07:00
Daniel Chao 0035199176 Improve partial-evaluation when amending null parents (#1836)
GraalVM's partial evaulator can't handle recursive calls into the same
node.

The current implementation around evaluating `VmNull` as a parent causes
the partial evaluator to bail out, leaving Pkl stuck in interpreter
mode.

This rewrites the various object literals to add individual
specializations for each type of default value we can see from a VmNull
parent.

Also:
* Fix `isTypeObjectClass` impl
* Fix bug when amending with generator object literal node with object params
2026-09-02 18:18:55 +00:00
Daniel Chao 2c2f50113d Make enclosing frame lookup more PE friendly (#1842)
The truffle compiler currently _always_ bails out of compilation for
`VmUtils.getFrame()` calls, because it determines that some branch
of this code can possibly return a materialized frame.

This adjusts the code so that the hot path (levelsUp == 0)
does not touch `VmUtils.getFrame()`, and avoids unnecessarily
materializing the frame.
2026-09-02 10:01:25 -07:00
Daniel Chao 9f28c1cb11 Make listing/mapping typecasts more PE-friendly (#1837)
This rewrites the listing/mapping typecast nodes to be more friendly
to partial-evaluation.

This is because the compiler bails out of deeply recursive calls.
2026-09-02 10:00:59 -07:00
Daniel Chao adee565a8c Mark protobuf module as deprecated (#1778)
This moves the protobuf module from experimental to deprecated

Rationale:

* This is very seldom used, if used at all
* Not fully fleshed out (this really should be paired with a protoc-gen-pkl, which doesn't exist)
* There is a ProtoJSON wire format that we can already render to
2026-09-02 09:26:51 -07:00
Jen Basch 2b697015c7 Add this as a self type (#1708) 2026-09-02 09:04:15 -07:00
Daniel Chao 1208fa9438 Improve reading of const members (#1835)
The previous implementation can cause the JIT compiler to permanently
bail out when reading const memebrs.
2026-08-28 09:18:56 -07:00
Jen Basch 1cfdd3d30d Fix native build (#1832) 2026-08-26 22:45:18 +00:00
Jen Basch cb01d6275b Correctly type check Class<T> (#1698) 2026-08-26 05:29:34 +00:00
Sergio Salvi a90eb74c2d Fix reflect crash on modules using glob imports (#1816)
This excludes glob imports when creating a module's mirror
2026-08-25 12:04:45 -07:00
Jen Basch ae9160df51 Enforce max collection sizes when decoding pkl-binary (#1831) 2026-08-25 08:28:28 -07:00
Jen Basch 79af6b1676 Sanitize block comment endings in generated Kotlin doc comments (#1830) 2026-08-25 08:27:30 -07:00
Sergio Salvi 7404f7d160 Fix block comment at EOF without trailing newline (#1828) 2026-08-21 15:46:37 -07:00
Daniel ChaoandFlorin Ungur 6551a59f9e Use XDG base directories and Known Folders on Windows (#1809)
This changes logic that previously read/wrote from `~/.pkl` to use
XDG base directories (all OSes), and Known Folders locations on Windows.

For example, Pkl will look for `settings.pkl` in:

1. `$XDG_CONFIG_HOME/pkl/settings.pkl`
2. `%APPDATA/pkl/settings.pkl`
3. `~/.pkl/settings.pkl`
4. Path pkl/settings/pkl within `$XDG_CONFIG_DIRS`
5. `/etc/xdg/pkl/settings.pkl`

---------

Co-authored-by: Florin Ungur <florin@florinungur.com>
2026-08-20 21:50:24 +00:00
Daniel ChaoandIslon Scherer 4dd37219c0 Fix name resolution of types in constraints (#1826)
This fixes an issue where type names are resolved incorrectly
in typealias constraints.

Co-authored-by: Islon Scherer <islonscherer@gmail.com>
2026-08-19 11:10:32 -07:00
Jen Basch ac43bdbb17 Add hex/binary/octal support to String.toInt() (#1808) 2026-08-18 09:44:02 -04:00
Jen BaschandDan Chao 762db144ce Enforce that abstract methods are implemented (#1785)
This adds a check that abstract members must be implemented.
If any members lack an implementation, an error is thrown describing
the missing members.

Co-authored-by: Dan Chao <dan.chao@apple.com>
2026-08-17 21:52:54 +00:00
Daniel Chao 99c9f53063 Remove community.adoc page (#1820)
This is being moved to pkl-lang.org
(see https://github.com/apple/pkl-lang.org/pull/172).
2026-08-10 11:23:24 -07:00
Daniel ChaoandIslon Scherer c5896d76d0 Split libpkl libraries between shared and static libs (#1812)
This adds a `libpkl-static.pc` so pkg-config users can link either
statically or dynamically via `pkg-config --libs libpkl`, or
`pkg-config --libs libpkl-static`.

Additionally, this fixes an issue where the replace tokens wasn't
doing anything, leaving the `@version@` token intact in the archive.

Additionally, this adds an `-install_name` when building the dylib for macOS.

Co-authored-by: Islon Scherer <islonscherer@gmail.com>
2026-08-07 15:47:16 +00:00
Jen Basch b3698683ab Upgrade to GraalVM 25.2.4 (#1804) 2026-08-03 09:24:53 -07:00
Daniel Chao 417c08ae11 Fix various issues in libpkl (#1805)
* Fix possible SIGSEGV from host process.
   - Require that the same `pkl_exec_t` be used in the same OS thread; returning meaningful error if this fails
* Remove `System.exitProcess(1)` logic in NativeTransport; this would kill the host process too and isn't an appropriate action for a received ProtocolException
* Allow multiple calls to pkl_init without pkl_close; this limitation doesn't really make any sense
* Prefer calling methods defined in graal_isolate.h, instead of creating the same method via CEntryPoint
* Add a CMakeLists.txt so that CLion can follow the code and provide proper diagnostics
* Improve doc comments; describe params as either in or out
2026-07-31 16:23:37 +00:00
Jen Basch 5333d55269 Skip loading known-bad certs (#1806) 2026-07-30 22:54:08 +00:00
043f0e129b Add documentation for libpkl (#1801)
This adds a new Antora module for libpkl, and updates the language
bindings documentation.

Co-authored-by: Jen Basch <jbasch94@gmail.com>
Co-authored-by: Islon Scherer <islonscherer@gmail.com>
2026-07-30 17:24:27 +00:00
Daniel Chao a55a3d7c33 Improve evaluation of typealiases (#1796)
This simplifies the resolution of names in typealias constraints.
This removes the existing logic around swapping out the frame's
owner/receiver, and instead favors resolving the variable at parse time.

* During variable resolution, create read variable nodes that read off of
  a receiver node
* Introduce `GetTypeAliasModuleNode` that provides the typealias's
  enclosing module
* Inject the enclosing module during typealias instantiation
2026-07-30 17:22:45 +00:00
Jen Basch 2ca2aaf9ec Add pkl project package --install to local cache (#1795) 2026-07-30 07:26:21 +00:00
Jen Basch 8ad5759630 Use EnumSet internal for Token type checks (#1803) 2026-07-29 16:09:52 -07:00
Rushikesh 871041e655 Avoid duplicate pkl-gradle publication (#1777)
The java-gradle-plugin already creates the pluginMaven publication. Applying the library publishing convention creates another publication with the same coordinates, causing an overwrite warning during Sonatype publishing.

Configure the shared POM metadata, validation, signing, and archive artifacts directly so pkl-gradle retains the convention behavior without creating the duplicate library publication.

Fixes #1742
2026-07-29 22:26:51 +00:00
Jen Basch 50a09f59dc Remove abstract properties (#1781) 2026-07-29 15:05:19 -07:00
Daniel Chao f6107b6e86 Fix libpkl and windows builds (#1799)
* Fix issue where linker can't find object files
* Fix issue where macOS libpkl builds time out due to exhausting host resources
2026-07-28 23:32:19 +00:00
Daniel Chao 79dd95c4e6 Fix deploy build (#1798)
* Fix issue where the DeployJob and GithubRelease jobs download
  artifacts using a glob that only matches one artifact
* Make NativeImageBuild task only produce outputs as declared by build;
  ensure there's no build_artifacts.txt, sources/ dir, etc.
2026-07-28 20:42:50 +00:00
Daniel Chao be03023499 Fix snippet test mismatch between hotspot and native test (#1797) 2026-07-28 18:18:36 +00:00
Islon Scherer 1a3dc52a96 Fix bug in force (#1790) 2026-07-28 09:19:15 +02:00
67df676359 Introduce C library for Pkl (#1238)
This uses native-image to generate a C library for Pkl.
This generated library from native-image is wrapped with our own library,
in `pkl.h`.

This produces a static and a dynamic library for each os/arch variant
that Pkl currently supports.

Co-authored-by: Kushal Pisavadia <kushal.p@apple.com>
Co-authored-by: Jen Basch <jbasch94@gmail.com>
Co-authored-by: Islon Scherer <i_desouzascherer@apple.com>
2026-07-25 04:15:26 +00:00
David TeslerandDan Chao 175e2b6273 Fix negative caret width rendering multi-line expression errors (#1793)
The expression preamble length was subtracted from endColumn even
when endColumn came from the already-stripped sourceLine.length(),
so multi-line frames got a negative caret width and String.repeat
threw. Apply the offset only to the frame-derived columns.

---------

Co-authored-by: Dan Chao <dan.chao@apple.com>
2026-07-24 20:57:05 +00:00
Islon SchererandDaniel Chao 2c9e3161d8 Import release notes for 0.32.1 (#1789) (#1791)
Co-authored-by: Daniel Chao <dan.chao@apple.com>
2026-07-23 19:06:15 +02:00
Daniel Chao 2242cc9c82 Fix variable resolution of object body params (#1788)
Fixes a regression introduced in Pkl 0.32.
2026-07-22 13:04:36 -07:00
Daniel Chao 03792be984 Add more third party licenses (#1784)
Some new third-party libs ended up in our distributions, but we didn't
add them to our third party notices.
2026-07-17 18:59:11 +00:00
Daniel Chao 3b5e9df0da Migrate Gradle delegated properties (#1782)
There were some more that we missed last time around
2026-07-16 15:41:28 -07:00
Kushal Pisavadia 50cfc0bcde Close intermediate redirect response bodies before following (#1749)
When following redirects, `RequestRewritingClient` discarded each 3xx
response without closing its body, leaking the underlying connection.

Close the body as soon as we know the status is a redirect and won't
be returned, so the too-many-redirects, missing-`Location`,
invalid-URI and downgrade error paths all release the connection too.

This prevents leaking one `BodyHandlers.ofInputStream()` connection
per hop. Now the code closes the body before following the redirect.
2026-07-16 21:11:53 +00:00
Jen BaschandVinayak 3d03a47933 Revert "Revert "Reject abstract members in non-abstract classes" (#1688)" (#1780)
This reverts commit 1bf00b84ea.

Co-authored-by: Vinayak <vinayak@vama.app>
2026-07-16 11:34:20 -07:00
dependabot[bot] a1421ff667 Bump shadowPlugin from 9.4.2 to 9.5.1 (#1772)
Bumps `shadowPlugin` from 9.4.2 to 9.5.1.

Updates `com.gradleup.shadow:com.gradleup.shadow.gradle.plugin` from 9.4.2 to 9.5.1
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.4.2...9.5.1)

Updates `com.gradleup.shadow` from 9.4.2 to 9.5.1
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.4.2...9.5.1)

---
updated-dependencies:
- dependency-name: com.gradleup.shadow:com.gradleup.shadow.gradle.plugin
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.gradleup.shadow
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 23:36:31 +00:00
dependabot[bot] 773e964ea2 Bump com.diffplug.spotless:spotless-plugin-gradle from 8.7.0 to 8.8.0 (#1773)
Bumps [com.diffplug.spotless:spotless-plugin-gradle](https://github.com/diffplug/spotless) from 8.7.0 to 8.8.0.
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/gradle/8.7.0...gradle/8.8.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-plugin-gradle
  dependency-version: 8.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 23:35:45 +00:00
dependabot[bot] c44cbc546b Bump nu.validator:validator from 26.6.24 to 26.7.3 (#1776)
Bumps [nu.validator:validator](https://github.com/validator/validator) from 26.6.24 to 26.7.3.
- [Release notes](https://github.com/validator/validator/releases)
- [Commits](https://github.com/validator/validator/compare/26.6.24...26.7.3)

---
updated-dependencies:
- dependency-name: nu.validator:validator
  dependency-version: 26.7.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 23:35:16 +00:00
dependabot[bot] 71a0b6d113 Bump gradle-wrapper from 9.6.0 to 9.6.1 (#1775)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.0...v9.6.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 23:34:59 +00:00
dependabot[bot] 4f2d2873ae Bump actions/setup-java from 5.4.0 to 5.5.0 in the first-party group (#1774)
Bumps the first-party group with 1 update: [actions/setup-java](https://github.com/actions/setup-java).


Updates `actions/setup-java` from 5.4.0 to 5.5.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/1bcf9fb12cf4aa7d266a90ae39939e61372fe520...0f481fcb613427c0f801b606911222b5b6f3083a)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: first-party
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 23:34:39 +00:00
Jen Basch 42f107881f Update dependabot schedule (#1771) 2026-07-14 16:23:14 -07:00
Jen Basch 71948d69c4 Upgrade to GraalVM 25.1.3, drop macOS Intel builds (#1769) 2026-07-13 15:52:02 -07:00
Daniel Chao 60b0d86b5f Fix eval -x of local members (#1760)
This fixes a regression where local members cannot be seen by the
expression evaluator.

Because variable are now parse-time resolved, this builds a truffle node
by first constructing a synthetic module of all defined local members,
and calls AstBuilder on this synthetic module first.
2026-07-11 00:03:15 +00:00
dependabot[bot] b4ca82bf36 Bump kotlinToolchain from 2.3.21 to 2.4.0 (#1678)
Bumps `kotlinToolchain` from 2.3.21 to 2.4.0.

Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 2.3.21 to 2.4.0
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.3.21...v2.4.0)

Updates `org.jetbrains.kotlin.plugin.serialization` from 2.3.21 to 2.4.0
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.3.21...v2.4.0)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.plugin.serialization
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 16:19:56 -07:00
dependabot[bot] 1cc47faac3 Bump junit from 6.1.0 to 6.1.1 (#1750)
Bumps `junit` from 6.1.0 to 6.1.1.

Updates `org.junit.jupiter:junit-jupiter-api` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.0...r6.1.1)

Updates `org.junit.jupiter:junit-jupiter-engine` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.0...r6.1.1)

Updates `org.junit.jupiter:junit-jupiter-params` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.0...r6.1.1)

Updates `org.junit.platform:junit-platform-launcher` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.0...r6.1.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.jupiter:junit-jupiter-params
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.junit.platform:junit-platform-launcher
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 15:51:53 -07:00
dependabot[bot] d6107c071b Bump com.palantir.javapoet:javapoet from 0.16.0 to 0.17.0 (#1751)
Bumps [com.palantir.javapoet:javapoet](https://github.com/palantir/javapoet) from 0.16.0 to 0.17.0.
- [Release notes](https://github.com/palantir/javapoet/releases)
- [Commits](https://github.com/palantir/javapoet/compare/0.16.0...0.17.0)

---
updated-dependencies:
- dependency-name: com.palantir.javapoet:javapoet
  dependency-version: 0.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 15:51:33 -07:00
dependabot[bot] f21175df9a Bump jline from 4.2.1 to 4.3.1 (#1752)
Bumps `jline` from 4.2.1 to 4.3.1.

Updates `org.jline:jline-reader` from 4.2.1 to 4.3.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.2.1...4.3.1)

Updates `org.jline:jline-terminal` from 4.2.1 to 4.3.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.2.1...4.3.1)

Updates `org.jline:jline-terminal-jni` from 4.2.1 to 4.3.1
- [Release notes](https://github.com/jline/jline3/releases)
- [Commits](https://github.com/jline/jline3/compare/4.2.1...4.3.1)

---
updated-dependencies:
- dependency-name: org.jline:jline-reader
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jline:jline-terminal-jni
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 15:51:11 -07:00
dependabot[bot] a59fd9d2ad Bump the codeql group with 2 updates (#1757)
Bumps the codeql group with 2 updates: [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/init](https://github.com/github/codeql-action).


Updates `github/codeql-action/analyze` from 4.36.2 to 4.36.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a)

Updates `github/codeql-action/init` from 4.36.2 to 4.36.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql
- dependency-name: github/codeql-action/init
  dependency-version: 4.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 15:09:48 -07:00
Jen Basch ed1d600779 Bump pkl.impl.ghactions to version 1.8.2 (#1756) 2026-07-09 13:50:20 -07:00
Daniel Chao 7a7f3abc50 Migrate Gradle deprecations (#1745) 2026-07-08 12:05:19 -07:00
Dan Chao 25d86db416 Update changelog with release notes link 2026-07-08 10:29:48 -07:00
Dan Chao 997cfc9ae2 Start next dev iteration 2026-07-08 10:29:48 -07:00
Dan Chao 9916c23cfc Prepare 0.32.0 release 2026-07-08 10:29:48 -07:00
742 changed files with 21037 additions and 9003 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ amends "pkl:Project"
dependencies {
["pkl.impl.ghactions"] {
uri = "package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.8.1"
uri = "package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.8.2"
}
["gha"] {
uri = "package://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1.4.0"
+8 -8
View File
@@ -3,30 +3,30 @@
"resolvedDependencies": {
"package://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1": {
"type": "remote",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1.7.0",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1.9.0",
"checksums": {
"sha256": "e3461c3f8d00a6a657f9fa4f391afdab4a53a2e528862a4d8624e545cd3cb2b1"
"sha256": "5947924cf94fae9d8cdca42ade3f86db2675842cab7254214e7c0e281ddb6908"
}
},
"package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1": {
"type": "remote",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.8.1",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.8.2",
"checksums": {
"sha256": "476cd931de6868b3f5e088ac741261d7beb9f4de3305564675510fd74ffb83f3"
"sha256": "12cca6326806ec37abc790a6707b6d907b4f8ba97628f2bc7fb327b5a6f68b49"
}
},
"package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1": {
"type": "remote",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1.2.0",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1.2.1",
"checksums": {
"sha256": "84c7feb391f4ac273a99dc89b8fd51dbcd21dbda4ce640f6908527f83acdd4d6"
"sha256": "6e752d1e84aff24c3a1801658f27f12af70840c39b3fe7bac509c8178d566d25"
}
},
"package://pkg.pkl-lang.org/pkl-pantry/pkl.github.dependabotManagedActions@1": {
"type": "remote",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.github.dependabotManagedActions@1.1.6",
"uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.github.dependabotManagedActions@1.1.9",
"checksums": {
"sha256": "85ad51d257bad4729f0b9edecade48fcad6f55bacd0fa36106782475e912919a"
"sha256": "467b91559f31ad7401c78d3df9569b5ec7aa4e1b49209f9f22d02bd72971e0bd"
}
},
"package://pkg.pkl-lang.org/pkl-pantry/com.github.dependabot@1": {
+9 -2
View File
@@ -5,14 +5,21 @@ updates:
default-days: 7
directory: /
schedule:
interval: weekly
interval: quarterly
- package-ecosystem: github-actions
cooldown:
default-days: 7
directory: /
groups:
first-party:
patterns:
- actions/*
codeql:
patterns:
- github/codeql-action/*
ignore:
- dependency-name: '*'
update-types:
- version-update:semver-major
schedule:
interval: weekly
interval: quarterly
+16 -9
View File
@@ -61,29 +61,36 @@ local gradleCheckJobs: PklJobs = new {
local buildNativeJobs: Mapping<String, BuildNativeJob> = new {
for (_dist in List("release", "snapshot")) {
for (_project in List("pkl-cli", "pkl-doc")) {
for (_project in List("pkl-cli", "pkl-doc", "libpkl")) {
for (_arch in List("amd64", "aarch64")) {
for (_os in List("macOS", "linux")) {
["\(_project)-\(_os)-\(_arch)-\(_dist)"] {
arch = _arch
os = _os
isRelease = _dist == "release"
project = _project
}
["\(_project)-linux-\(_arch)-\(_dist)"] {
arch = _arch
os = "linux"
isRelease = _dist == "release"
project = _project
buildDir = if (_project == "libpkl") "libpkl/build/native-libs/**/*" else "\(_project)*/build/executable/**/*"
}
}
["\(_project)-macOS-aarch64-\(_dist)"] {
arch = "aarch64"
os = "macOS"
isRelease = _dist == "release"
project = _project
}
["\(_project)-alpine-linux-amd64-\(_dist)"] {
arch = "amd64"
os = "linux"
musl = true
isRelease = _dist == "release"
project = _project
buildDir = if (_project == "libpkl") "libpkl*/build/distributions/**/*" else "\(_project)*/build/executable/**/*"
}
["\(_project)-windows-amd64-\(_dist)"] {
arch = "amd64"
os = "windows"
isRelease = _dist == "release"
project = _project
buildDir = if (_project == "libpkl") "libpkl*/build/distributions/**/*" else "\(_project)*/build/executable/**/*"
}
}
}
@@ -230,7 +237,7 @@ dependabot {
new {
`package-ecosystem` = "gradle"
schedule {
interval = "weekly"
interval = "quarterly"
}
cooldown {
`default-days` = 7
+2 -1
View File
@@ -16,6 +16,7 @@ local command =
}.join(" ")
steps {
catalog.`actions/checkout@v6`
new {
name = "gradle build java executables"
shell = "bash"
@@ -24,7 +25,7 @@ steps {
(catalog.`actions/upload-artifact@v5`) {
name = "Upload executable artifacts"
with {
name = "executable-java"
name = "deploy-artifacts-java-executables"
path = "*/build/executable/**/*"
}
}
+15 -9
View File
@@ -1,7 +1,6 @@
extends "GradleJob.pkl"
import "@gha/catalog.pkl"
import "@gha/context.pkl"
/// Whether to link to musl. Otherwise, links to glibc.
musl: Boolean(implies(module.os == "linux")) = false
@@ -9,11 +8,12 @@ musl: Boolean(implies(module.os == "linux")) = false
/// The Gradle project under which to generate the executable
project: String
/// Path pattern for artifact uploads (glob passed to actions/upload-artifact)
buildDir: String = "\(project)*/build/executable/**/*"
local needsCCompiler = project == "libpkl"
extraGradleArgs {
when (os == "macOS" && arch == "amd64") {
"-Dpkl.targetArch=\(module.arch)"
#""-Dpkl.native--native-compiler-path=${GITHUB_WORKSPACE}/.github/scripts/cc_macos_amd64.sh""#
}
when (musl) {
"-Dpkl.musl=true"
}
@@ -44,22 +44,28 @@ steps {
run = #"git status || git config --system --add safe.directory "$GITHUB_WORKSPACE""#
}
}
when (needsCCompiler && os == "windows") {
new {
name = "Set up MSVC"
uses = "ilammy/msvc-dev-cmd@v1"
}
}
new {
name = "gradle buildNative"
shell = "bash"
run = "./gradlew \(module.gradleArgs) \(project):buildNative"
}
(catalog.`actions/upload-artifact@v5`) {
name = "Upload executable artifacts"
name = "Upload built artifacts"
with {
name =
if (musl)
"executable-\(project)-alpine-\(module.os)-\(module.arch)"
"deploy-artifacts-\(project)-alpine-\(module.os)-\(module.arch)"
else
"executable-\(project)-\(module.os)-\(module.arch)"
"deploy-artifacts-\(project)-\(module.os)-\(module.arch)"
// Need to insert a wildcard to make actions/upload-artifact preserve the folder hierarchy.
// See https://github.com/actions/upload-artifact/issues/206
path = "\(project)*/build/executable/**/*"
path = module.buildDir
}
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ steps {
catalog.`actions/checkout@v6`
(catalog.`actions/download-artifact@v6`) {
with {
pattern = "executable-**"
pattern = "deploy-artifacts-**"
`merge-multiple` = true
}
}
+2 -5
View File
@@ -14,7 +14,7 @@ fixed job {
steps {
(catalog.`actions/download-artifact@v6`) {
with {
pattern = "executable-**"
pattern = "deploy-artifacts-**"
`merge-multiple` = true
}
}
@@ -29,15 +29,12 @@ fixed job {
// language=bash
run =
#"""
# exclude build_artifacts.txt from publish
rm -f */build/executable/*.build_artifacts.txt
find */build/executable/* -type d | xargs rm -rf
gh release create ${TAG_NAME} \
--title "${TAG_NAME}" \
--target "${GIT_SHA}" \
--verify-tag \
--notes "Release notes: https://pkl-lang.org/main/current/release-notes/changelog.html#release-${TAG_NAME}" \
*/build/executable/*
*/build/executable/* */build/distributions/*
"""#
}
}
-2
View File
@@ -1,2 +0,0 @@
#!/usr/bin/env bash
clang -arch x86_64 "$@"
+6 -17
View File
@@ -36,23 +36,12 @@ fi
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl tarball and signature
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz" -o /tmp/musl.tar.gz
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz.asc" -o /tmp/musl.tar.gz.asc
# Import musl GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 836489290BB6B70F99FFDA0556BCDB593020450F
# Verify GPG signature
echo "Verifying musl GPG signature..."
gpg --verify /tmp/musl.tar.gz.asc /tmp/musl.tar.gz
mkdir -p "/tmp/dep_musl-${MUSL_VERSION}"
cd "/tmp/dep_musl-${MUSL_VERSION}"
# shellcheck disable=SC2002
cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC .
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
+7 -5
View File
@@ -19,22 +19,24 @@ jobs:
- name: EnricoMi/publish-unit-test-result-action@v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
- name: actions/checkout@v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: actions/create-github-app-token@v2
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
- name: actions/download-artifact@v6
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- name: actions/setup-java@v5
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
- name: actions/upload-artifact@v5
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- name: dawidd6/action-download-artifact@v11
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
- name: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
- name: github/codeql-action/init@v4
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
- name: gradle/actions/dependency-submission@v6
uses: gradle/actions/dependency-submission@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6
uses: gradle/actions/dependency-submission@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6
- name: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
- name: ilammy/msvc-dev-cmd@v1
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1
+459 -269
View File
@@ -19,11 +19,11 @@ jobs:
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -60,11 +60,11 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -100,10 +100,10 @@ jobs:
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -126,11 +126,11 @@ jobs:
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -166,11 +166,11 @@ jobs:
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -185,13 +185,16 @@ jobs:
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: gradle build java executables
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-java
name: deploy-artifacts-java-executables
path: '*/build/executable/**/*'
- name: Upload Test Result XML
if: '!cancelled()'
@@ -206,53 +209,6 @@ jobs:
name: test-results-html-java-executables-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-macOS-amd64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.targetArch=amd64 "-Dpkl.native--native-compiler-path=${GITHUB_WORKSPACE}/.github/scripts/cc_macos_amd64.sh" pkl-cli:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-macOS-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-amd64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
@@ -260,10 +216,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -283,10 +239,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-linux-amd64
name: deploy-artifacts-pkl-cli-linux-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -303,53 +259,6 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-cli-macOS-aarch64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-macOS-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-linux-aarch64-snapshot:
runs-on: ubuntu-24.04-arm
env:
@@ -357,10 +266,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -380,10 +289,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-linux-aarch64
name: deploy-artifacts-pkl-cli-linux-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -400,15 +309,62 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-cli-macOS-aarch64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-pkl-cli-macOS-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-alpine-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -463,23 +419,12 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl tarball and signature
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz" -o /tmp/musl.tar.gz
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz.asc" -o /tmp/musl.tar.gz.asc
# Import musl GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 836489290BB6B70F99FFDA0556BCDB593020450F
# Verify GPG signature
echo "Verifying musl GPG signature..."
gpg --verify /tmp/musl.tar.gz.asc /tmp/musl.tar.gz
mkdir -p "/tmp/dep_musl-${MUSL_VERSION}"
cd "/tmp/dep_musl-${MUSL_VERSION}"
# shellcheck disable=SC2002
cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC .
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
@@ -499,10 +444,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-alpine-linux-amd64
name: deploy-artifacts-pkl-cli-alpine-linux-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -523,10 +468,10 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -544,10 +489,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-windows-amd64
name: deploy-artifacts-pkl-cli-windows-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -562,53 +507,6 @@ jobs:
name: test-results-html-pkl-cli-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-macOS-amd64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.targetArch=amd64 "-Dpkl.native--native-compiler-path=${GITHUB_WORKSPACE}/.github/scripts/cc_macos_amd64.sh" pkl-doc:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-macOS-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-amd64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
@@ -616,10 +514,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -639,10 +537,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-linux-amd64
name: deploy-artifacts-pkl-doc-linux-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -659,53 +557,6 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-doc-macOS-aarch64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-macOS-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-linux-aarch64-snapshot:
runs-on: ubuntu-24.04-arm
env:
@@ -713,10 +564,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -736,10 +587,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-linux-aarch64
name: deploy-artifacts-pkl-doc-linux-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -756,15 +607,62 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-doc-macOS-aarch64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-pkl-doc-macOS-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-alpine-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -819,23 +717,12 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl tarball and signature
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz" -o /tmp/musl.tar.gz
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz.asc" -o /tmp/musl.tar.gz.asc
# Import musl GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 836489290BB6B70F99FFDA0556BCDB593020450F
# Verify GPG signature
echo "Verifying musl GPG signature..."
gpg --verify /tmp/musl.tar.gz.asc /tmp/musl.tar.gz
mkdir -p "/tmp/dep_musl-${MUSL_VERSION}"
cd "/tmp/dep_musl-${MUSL_VERSION}"
# shellcheck disable=SC2002
cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC .
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
@@ -855,10 +742,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-alpine-linux-amd64
name: deploy-artifacts-pkl-doc-alpine-linux-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -879,10 +766,10 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -900,10 +787,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-windows-amd64
name: deploy-artifacts-pkl-doc-windows-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -918,6 +805,306 @@ jobs:
name: test-results-html-pkl-doc-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Fix git ownership
run: git status || git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-linux-amd64
path: libpkl/build/native-libs/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-linux-amd64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-linux-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
libpkl-linux-aarch64-snapshot:
runs-on: ubuntu-24.04-arm
env:
LANG: en_US.UTF-8
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Fix git ownership
run: git status || git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-linux-aarch64
path: libpkl/build/native-libs/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-linux-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-linux-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
libpkl-macOS-aarch64-snapshot:
if: github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-macOS-aarch64
path: libpkl*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-alpine-linux-amd64-snapshot:
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Install musl and zlib
run: |
set -e
mkdir -p ~/staticdeps/
ZLIB_VERSION="1.2.13"
MUSL_VERSION="1.2.5"
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
# Download zlib tarball and signature
curl -Lf "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" -o /tmp/zlib.tar.gz
curl -Lf "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz.asc" -o /tmp/zlib.tar.gz.asc
# Import zlib GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 5ED46A6721D365587791E2AA783FCD8E58BCAFBA
# Verify GPG signature
echo "Verifying zlib GPG signature..."
gpg --verify /tmp/zlib.tar.gz.asc /tmp/zlib.tar.gz
mkdir -p "/tmp/dep_zlib-${ZLIB_VERSION}"
cd "/tmp/dep_zlib-${ZLIB_VERSION}"
# shellcheck disable=SC2002
cat /tmp/zlib.tar.gz | tar --strip-components=1 -xzC .
echo "zlib-${ZLIB_VERSION}: configure..."
./configure --static --prefix="$HOME"/staticdeps > /dev/null
echo "zlib-${ZLIB_VERSION}: make..."
make -s -j4
echo "zlib-${ZLIB_VERSION}: make install..."
make -s install
rm -rf /tmp/dep_zlib-${ZLIB_VERSION}
fi
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
echo "musl-${MUSL_VERSION}: make..."
make -s -j4
echo "musl-${MUSL_VERSION}: make install..."
make -s install
rm -rf "/tmp/dep_musl-${MUSL_VERSION}"
# native-image expects to find an executable at this path.
ln -s ~/staticdeps/bin/musl-gcc ~/staticdeps/bin/x86_64-linux-musl-gcc
fi
echo "${HOME}/staticdeps/bin" >> "$GITHUB_PATH"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-alpine-linux-amd64
path: libpkl*/build/distributions/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-alpine-linux-amd64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-alpine-linux-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-windows-amd64-snapshot:
runs-on: windows-latest
env:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Set up MSVC
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-windows-amd64
path: libpkl*/build/distributions/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-windows-amd64-snapshot
path: '**/build/test-results/**/*.xml'
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
publish-test-results:
if: '!cancelled()'
needs:
@@ -925,18 +1112,21 @@ jobs:
- gradle-check-windows
- gradle-compatibility
- java-executables-snapshot
- pkl-cli-macOS-amd64-snapshot
- pkl-cli-linux-amd64-snapshot
- pkl-cli-macOS-aarch64-snapshot
- pkl-cli-linux-aarch64-snapshot
- pkl-cli-macOS-aarch64-snapshot
- pkl-cli-alpine-linux-amd64-snapshot
- pkl-cli-windows-amd64-snapshot
- pkl-doc-macOS-amd64-snapshot
- pkl-doc-linux-amd64-snapshot
- pkl-doc-macOS-aarch64-snapshot
- pkl-doc-linux-aarch64-snapshot
- pkl-doc-macOS-aarch64-snapshot
- pkl-doc-alpine-linux-amd64-snapshot
- pkl-doc-windows-amd64-snapshot
- libpkl-linux-amd64-snapshot
- libpkl-linux-aarch64-snapshot
- libpkl-macOS-aarch64-snapshot
- libpkl-alpine-linux-amd64-snapshot
- libpkl-windows-amd64-snapshot
permissions:
checks: write
runs-on: ubuntu-latest
+9 -9
View File
@@ -15,15 +15,15 @@ jobs:
security-events: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
- uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
languages: actions
build-mode: none
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
category: /language:actions
analyze-java-kotlin:
@@ -32,15 +32,15 @@ jobs:
security-events: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
- uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
languages: java-kotlin
build-mode: autobuild
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
category: /language:java-kotlin
analyze-javascript-typescript:
@@ -49,14 +49,14 @@ jobs:
security-events: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
- uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
languages: javascript-typescript
build-mode: none
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
category: /language:javascript-typescript
+496 -300
View File
File diff suppressed because it is too large. Load diff
+461 -267
View File
@@ -13,11 +13,11 @@ jobs:
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -56,11 +56,11 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -92,54 +92,6 @@ jobs:
name: test-results-html-gradle-check-windows
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-macOS-amd64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-amd64]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-amd64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.targetArch=amd64 "-Dpkl.native--native-compiler-path=${GITHUB_WORKSPACE}/.github/scripts/cc_macos_amd64.sh" pkl-cli:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-macOS-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-amd64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-amd64]')
runs-on: ubuntu-latest
@@ -148,10 +100,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -171,10 +123,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-linux-amd64
name: deploy-artifacts-pkl-cli-linux-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -192,54 +144,6 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-cli-macOS-aarch64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-aarch64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-macOS-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-linux-aarch64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-aarch64]')
runs-on: ubuntu-24.04-arm
@@ -248,10 +152,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -271,10 +175,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-linux-aarch64
name: deploy-artifacts-pkl-cli-linux-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -292,16 +196,64 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-cli-macOS-aarch64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-cli-macOS-aarch64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-pkl-cli-macOS-aarch64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-cli-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-cli-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-cli-alpine-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-cli]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-cli-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-cli-alpine-linux-amd64]')
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -356,23 +308,12 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl tarball and signature
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz" -o /tmp/musl.tar.gz
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz.asc" -o /tmp/musl.tar.gz.asc
# Import musl GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 836489290BB6B70F99FFDA0556BCDB593020450F
# Verify GPG signature
echo "Verifying musl GPG signature..."
gpg --verify /tmp/musl.tar.gz.asc /tmp/musl.tar.gz
mkdir -p "/tmp/dep_musl-${MUSL_VERSION}"
cd "/tmp/dep_musl-${MUSL_VERSION}"
# shellcheck disable=SC2002
cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC .
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
@@ -392,10 +333,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-alpine-linux-amd64
name: deploy-artifacts-pkl-cli-alpine-linux-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -418,10 +359,10 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -439,10 +380,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-cli:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-cli-windows-amd64
name: deploy-artifacts-pkl-cli-windows-amd64
path: pkl-cli*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -458,54 +399,6 @@ jobs:
name: test-results-html-pkl-cli-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-macOS-amd64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-amd64]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-amd64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.targetArch=amd64 "-Dpkl.native--native-compiler-path=${GITHUB_WORKSPACE}/.github/scripts/cc_macos_amd64.sh" pkl-doc:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-macOS-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-amd64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-amd64]')
runs-on: ubuntu-latest
@@ -514,10 +407,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -537,10 +430,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-linux-amd64
name: deploy-artifacts-pkl-doc-linux-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -558,54 +451,6 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-doc-macOS-aarch64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-aarch64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-macOS-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-linux-aarch64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-aarch64]')
runs-on: ubuntu-24.04-arm
@@ -614,10 +459,10 @@ jobs:
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -637,10 +482,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-linux-aarch64
name: deploy-artifacts-pkl-doc-linux-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -658,16 +503,64 @@ jobs:
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
pkl-doc-macOS-aarch64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-aarch64]') || contains(github.event.pull_request.body, '[native-pkl-doc-macOS-aarch64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-pkl-doc-macOS-aarch64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-pkl-doc-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-pkl-doc-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
pkl-doc-alpine-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-pkl-doc]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-doc-linux-amd64]') || contains(github.event.pull_request.body, '[native-pkl-doc-alpine-linux-amd64]')
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -722,23 +615,12 @@ jobs:
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl tarball and signature
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz" -o /tmp/musl.tar.gz
curl -Lf "https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz.asc" -o /tmp/musl.tar.gz.asc
# Import musl GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 836489290BB6B70F99FFDA0556BCDB593020450F
# Verify GPG signature
echo "Verifying musl GPG signature..."
gpg --verify /tmp/musl.tar.gz.asc /tmp/musl.tar.gz
mkdir -p "/tmp/dep_musl-${MUSL_VERSION}"
cd "/tmp/dep_musl-${MUSL_VERSION}"
# shellcheck disable=SC2002
cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC .
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
@@ -758,10 +640,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-alpine-linux-amd64
name: deploy-artifacts-pkl-doc-alpine-linux-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -784,10 +666,10 @@ jobs:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
@@ -805,10 +687,10 @@ jobs:
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:buildNative
- name: Upload executable artifacts
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: executable-pkl-doc-windows-amd64
name: deploy-artifacts-pkl-doc-windows-amd64
path: pkl-doc*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
@@ -824,24 +706,336 @@ jobs:
name: test-results-html-pkl-doc-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-libpkl]') || contains(github.event.pull_request.body, '[native-libpkl-linux]') || contains(github.event.pull_request.body, '[native-libpkl-linux-amd64]') || contains(github.event.pull_request.body, '[native-libpkl-linux-amd64]')
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Fix git ownership
run: git status || git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-linux-amd64
path: libpkl/build/native-libs/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-linux-amd64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-linux-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
libpkl-linux-aarch64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-libpkl]') || contains(github.event.pull_request.body, '[native-libpkl-linux]') || contains(github.event.pull_request.body, '[native-libpkl-linux-aarch64]') || contains(github.event.pull_request.body, '[native-libpkl-linux-aarch64]')
runs-on: ubuntu-24.04-arm
env:
LANG: en_US.UTF-8
steps:
- name: Install deps
run: dnf install -y git binutils gcc glibc-devel zlib-devel libstdc++-static glibc-langpack-en
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Fix git ownership
run: git status || git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-linux-aarch64
path: libpkl/build/native-libs/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-linux-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-linux-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
container:
image: redhat/ubi8:8.10
libpkl-macOS-aarch64-snapshot:
if: (contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-libpkl]') || contains(github.event.pull_request.body, '[native-libpkl-macOS]') || contains(github.event.pull_request.body, '[native-libpkl-macOS-aarch64]') || contains(github.event.pull_request.body, '[native-libpkl-macOS-aarch64]')) && github.repository_owner == 'apple'
runs-on:
- self-hosted
- macos
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: aarch64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-macOS-aarch64
path: libpkl*/build/executable/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-macOS-aarch64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-macOS-aarch64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-alpine-linux-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-libpkl]') || contains(github.event.pull_request.body, '[native-libpkl-linux]') || contains(github.event.pull_request.body, '[native-libpkl-linux-amd64]') || contains(github.event.pull_request.body, '[native-libpkl-linux-amd64]') || contains(github.event.pull_request.body, '[native-libpkl-alpine-linux-amd64]')
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Install musl and zlib
run: |
set -e
mkdir -p ~/staticdeps/
ZLIB_VERSION="1.2.13"
MUSL_VERSION="1.2.5"
# install zlib
if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
# Download zlib tarball and signature
curl -Lf "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" -o /tmp/zlib.tar.gz
curl -Lf "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz.asc" -o /tmp/zlib.tar.gz.asc
# Import zlib GPG key
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 5ED46A6721D365587791E2AA783FCD8E58BCAFBA
# Verify GPG signature
echo "Verifying zlib GPG signature..."
gpg --verify /tmp/zlib.tar.gz.asc /tmp/zlib.tar.gz
mkdir -p "/tmp/dep_zlib-${ZLIB_VERSION}"
cd "/tmp/dep_zlib-${ZLIB_VERSION}"
# shellcheck disable=SC2002
cat /tmp/zlib.tar.gz | tar --strip-components=1 -xzC .
echo "zlib-${ZLIB_VERSION}: configure..."
./configure --static --prefix="$HOME"/staticdeps > /dev/null
echo "zlib-${ZLIB_VERSION}: make..."
make -s -j4
echo "zlib-${ZLIB_VERSION}: make install..."
make -s install
rm -rf /tmp/dep_zlib-${ZLIB_VERSION}
fi
# install musl
if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
# Download musl git repo
cd /tmp
git clone https://git.musl-libc.org/git/musl /tmp/musl
cd /tmp/musl
git checkout "v$MUSL_VERSION"
echo "musl-${MUSL_VERSION}: configure..."
./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null
echo "musl-${MUSL_VERSION}: make..."
make -s -j4
echo "musl-${MUSL_VERSION}: make install..."
make -s install
rm -rf "/tmp/dep_musl-${MUSL_VERSION}"
# native-image expects to find an executable at this path.
ln -s ~/staticdeps/bin/musl-gcc ~/staticdeps/bin/x86_64-linux-musl-gcc
fi
echo "${HOME}/staticdeps/bin" >> "$GITHUB_PATH"
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -Dpkl.musl=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-alpine-linux-amd64
path: libpkl*/build/distributions/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-alpine-linux-amd64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-alpine-linux-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
libpkl-windows-amd64-snapshot:
if: contains(github.event.pull_request.body, '[native]') || contains(github.event.pull_request.body, '[native-libpkl]') || contains(github.event.pull_request.body, '[native-libpkl-windows]') || contains(github.event.pull_request.body, '[native-libpkl-windows-amd64]') || contains(github.event.pull_request.body, '[native-libpkl-windows-amd64]')
runs-on: windows-latest
env:
LANG: en_US.UTF-8
JAVA_HOME: /jdk
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '25'
distribution: temurin
architecture: x64
- uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with: {}
- name: Set DEBUG_ARGS env var
env:
RUNNER_DEBUG: ${{ runner.debug }}
shell: bash
run: |-
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi
- name: Set up MSVC
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1
- name: gradle buildNative
shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true libpkl:buildNative
- name: Upload built artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: deploy-artifacts-libpkl-windows-amd64
path: libpkl*/build/distributions/**/*
- name: Upload Test Result XML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-xml-libpkl-windows-amd64-snapshot
path: '**/build/test-results/**/*.xml'
if-no-files-found: ignore
- name: Upload Test Result HTML
if: '!cancelled()'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: test-results-html-libpkl-windows-amd64-snapshot
path: '**/build/reports/tests/**/*'
if-no-files-found: ignore
verdict:
if: always()
needs:
- check-pkl-github-actions
- gradle-check
- gradle-check-windows
- pkl-cli-macOS-amd64-snapshot
- pkl-cli-linux-amd64-snapshot
- pkl-cli-macOS-aarch64-snapshot
- pkl-cli-linux-aarch64-snapshot
- pkl-cli-macOS-aarch64-snapshot
- pkl-cli-alpine-linux-amd64-snapshot
- pkl-cli-windows-amd64-snapshot
- pkl-doc-macOS-amd64-snapshot
- pkl-doc-linux-amd64-snapshot
- pkl-doc-macOS-aarch64-snapshot
- pkl-doc-linux-aarch64-snapshot
- pkl-doc-macOS-aarch64-snapshot
- pkl-doc-alpine-linux-amd64-snapshot
- pkl-doc-windows-amd64-snapshot
- libpkl-linux-amd64-snapshot
- libpkl-linux-aarch64-snapshot
- libpkl-macOS-aarch64-snapshot
- libpkl-alpine-linux-amd64-snapshot
- libpkl-windows-amd64-snapshot
runs-on: ubuntu-latest
steps:
- name: Failure verdict
@@ -861,7 +1055,7 @@ jobs:
check-pkl-github-actions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Setup Pkl
+466 -273
View File
File diff suppressed because it is too large. Load diff
+502 -306
View File
File diff suppressed because it is too large. Load diff
+5 -1
View File
@@ -12,6 +12,7 @@
<option name="REPORT_FIELDS" value="true" />
</inspection_tool>
<inspection_tool class="ClassCanBeRecord" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ClassEscapesItsScope" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CustomRegExpInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myConfigurations">
<list>
@@ -96,6 +97,9 @@
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
</inspection_tool>
<inspection_tool class="SameParameterValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ShellCheck" enabled="true" level="ERROR" enabled_by_default="true">
<shellcheck_settings value="SC2129" />
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
@@ -104,4 +108,4 @@
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="dd497f47-d38f-3fab-9ed7-eabe699620c8" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
</profile>
</component>
</component>
+24
View File
@@ -0,0 +1,24 @@
# This file is only used to configure CLion with proper include paths.
# Pkl builds everything through Gradle, not CMake.
cmake_minimum_required(VERSION 4.2)
project(pkl C)
set(CMAKE_C_STANDARD 11)
file(GLOB NATIVE_LIB_INCLUDE_DIRS CONFIGURE_DEPENDS
libpkl/build/native-libs/*/include)
file(GLOB NATIVE_IMAGE_BUILD_DIRS CONFIGURE_DEPENDS
libpkl/build/tmp/native-image-build/*)
include_directories(${NATIVE_LIB_INCLUDE_DIRS})
include_directories(${NATIVE_IMAGE_BUILD_DIRS})
include_directories(libpkl/src/main/c/include)
file(GLOB_RECURSE GENERATED_HEADER_FILES CONFIGURE_DEPENDS
libpkl/build/tmp/native-libs/*/*.h)
add_executable(pkl
${GENERATED_HEADER_FILES}
libpkl/src/main/c/include/pkl.h
libpkl/src/main/c/pkl.c
libpkl/src/nativeTest/c/test_pkl.c)
-77
View File
@@ -1,77 +0,0 @@
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our
project and our community a harassment-free experience for everyone,
regardless of age, body size, disability, ethnicity, sex
characteristics, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual
attention or advances
* Trolling, insulting/derogatory comments, and personal or political
attacks
* Public or private harassment
* Publishing others’ private information, such as a physical or
electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of
acceptable behavior and are expected to take appropriate and fair
corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct, or to ban
temporarily or permanently any contributor for other behaviors that they
deem inappropriate, threatening, offensive, or harmful.
### Scope
This Code of Conduct applies within all project spaces, and it also
applies when an individual is representing the project or its community
in public spaces. Examples of representing a project or community
include using an official project e-mail address, posting via an
official social media account, or acting as an appointed representative
at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may
be reported by contacting the open source team at
opensource-conduct@group.apple.com. All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. The project team is obligated to
maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted
separately.
Project maintainers who do not follow or enforce the Code of Conduct in
good faith may face temporary or permanent repercussions as determined
by other members of the project’s leadership.
### Attribution
This Code of Conduct is adapted from the
[Contributor Covenant](https://www.contributor-covenant.org), version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+8 -1
View File
@@ -53,7 +53,6 @@ gw pkl-cli:buildNative # build native executable for current platform
pkl-cli/build/executable/jpkl # run Java executable
pkl-cli/build/executable/pkl-macos-aarch64 # run Mac executable
pkl-cli/build/executable/pkl-macos-amd64 # run Intel Mac executable
pkl-cli/build/executable/pkl-linux-amd64 # run Linux executable
pkl-cli/build/executable/pkl-alpine-linux-amd64 # run Alpine Linux executable
pkl-cli/build/executable/pkl-windows-amd64.exe # run Windows executable
@@ -94,6 +93,14 @@ There is an IntelliJ plugin meant for development on the Pkl project itself loca
See https://github.com/apple/pkl-project-commons?tab=readme-ov-file#internal-intellij-plugin[its readme] for instructions on how to set it up.
== Working on libpkl
libpkl is a native library written in C, and is best written in CLion, or VSCode.
IntelliJ has limited support for editing C sources.
There is a CMakeLists.txt file whose only job is to configure CLion with proper include paths.
== Resources
For automated build setup examples see our https://github.com/apple/pkl/blob/main/.github/[GitHub Actions] jobs like our https://github.com/apple/pkl/blob/main/.github/jobs/BuildNativeJob.pkl[BuildNativeJob.pkl], where we build Pkl automatically.
+45
View File
@@ -363,3 +363,48 @@ Copyright © 2008-2010 Andrey Somov
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License.You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
25) JSpecify (https://jspecify.dev)
POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
Copyright 2022 The JSpecify Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
26) mordant
POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
Copyright 2018 AJ Alt
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
27) colormath
License URL: https://github.com/ajalt/colormath/blob/master/LICENSE
Copyright 2021 AJ Alt
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+59 -28
View File
@@ -15,7 +15,10 @@
*/
@file:Suppress("MemberVisibilityCanBePrivate")
import Target.Arch
import Target.OS
import java.io.File
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
@@ -24,6 +27,7 @@ import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.testing.Test
import org.gradle.internal.extensions.stdlib.capitalized
import org.gradle.internal.os.OperatingSystem
import org.gradle.jvm.toolchain.*
import org.gradle.kotlin.dsl.*
import org.gradle.kotlin.dsl.support.serviceOf
@@ -70,31 +74,28 @@ const val PKL_TEST_ALL_JDKS = false
// `buildInfo` in main build scripts
// `project.extensions.getByType<BuildInfo>()` in precompiled script plugins
open class BuildInfo(private val project: Project) {
inner class GraalVm(val arch: String) {
inner class GraalVm(
val osName: String,
val arch: String,
val version: String,
val graalJdkVersion: String,
) {
val homeDir: String by lazy {
System.getenv("GRAALVM_HOME") ?: "${System.getProperty("user.home")}/.graalvm"
}
val version: String by lazy { libs.findVersion("graalVm").get().toString() }
val graalVmJdkVersion: String by lazy { libs.findVersion("graalVmJdkVersion").get().toString() }
val osName: String by lazy {
when {
os.isMacOsX -> "macos"
os.isLinux -> "linux"
os.isWindows -> "windows"
else -> throw RuntimeException("${os.familyName} is not supported.")
}
}
val graalVmInnovation: String by lazy { libs.findVersion("graalVmInnovation").get().toString() }
val baseName: String by lazy {
"graalvm-community-jdk-${graalVmJdkVersion}_${osName}-${arch}_bin"
}
val graalMajorVersion: String = graalJdkVersion.split(".").first()
val downloadUrl: String by lazy {
val extension = if (os.isWindows) "zip" else "tar.gz"
"https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${graalVmJdkVersion}/$baseName.$extension"
val ext = if (os.isWindows) "zip" else "tar.gz"
val platformArch = if (arch == "amd64") "x64" else arch
"https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-$version/graalvm-community-jdk-${graalMajorVersion}i${graalVmInnovation}-${graalJdkVersion}_${osName}-${platformArch}_bin.$ext"
}
val downloadFile: File by lazy {
@@ -105,35 +106,50 @@ open class BuildInfo(private val project: Project) {
val installDir: File by lazy { File(homeDir, baseName) }
val baseDir: String by lazy {
if (os.isMacOsX) "$installDir/Contents/Home" else installDir.toString()
if (os.isMacOS) "$installDir/Contents/Home" else installDir.toString()
}
}
/** The target machine to build, defaulting to the host system machine. */
val targetMachine: Target by lazy { Target.from(os = os, arch = targetArch, musl = musl) }
/** The target architecture to build, defaulting to the system architecture. */
val targetArch by lazy { System.getProperty("pkl.targetArch") ?: arch }
val targetArch: Arch by lazy { System.getProperty("pkl.targetArch")?.let(Arch::fromName) ?: arch }
/** Tells if this is a cross-arch build (e.g. targeting amd64 when on an aarch64 machine). */
val isCrossArch by lazy { arch != targetArch }
/** Tells if cross-arch builds are supported on this machine. */
val isCrossArchSupported by lazy { os.isMacOsX }
val isCrossArchSupported by lazy { os.isMacOS }
/** Whether to build native executables using the musl toolchain or not. */
val musl: Boolean by lazy { java.lang.Boolean.getBoolean("pkl.musl") }
/** Same logic as [org.gradle.internal.os.OperatingSystem#arch], which is protected. */
val arch: String by lazy {
val arch: Arch by lazy {
when (val arch = System.getProperty("os.arch")) {
"x86" -> "i386"
"x86_64" -> "amd64"
"powerpc" -> "ppc"
else -> arch
"x86_64",
"amd64" -> Arch.AMD64
"aarch64" -> Arch.AARCH64
else -> throw GradleException("Cannot build Pkl on arch: $arch")
}
}
val graalVmAarch64: GraalVm = GraalVm("aarch64")
val graalVmAarch64: GraalVm by lazy { createGraalVm("aarch64") }
val graalVmAmd64: GraalVm = GraalVm("x64")
val graalVmAmd64: GraalVm by lazy { createGraalVm("x64") }
private fun createGraalVm(arch: String): GraalVm {
val osName =
when {
os.isMacOS -> "macos"
os.isLinux -> "linux"
os.isWindows -> "windows"
else -> throw RuntimeException("Unsupported OS for GraalVM: ${os.name}")
}
val version = libs.findVersion("graalVm").get().toString()
val graalJdkVersion = libs.findVersion("graalVmJdkVersion").get().toString()
return GraalVm(osName, arch, version, graalJdkVersion)
}
val isCiBuild: Boolean by lazy { System.getenv("CI") != null }
@@ -368,8 +384,14 @@ open class BuildInfo(private val project: Project) {
File(System.getProperty("user.home"), "staticdeps/bin/x86_64-linux-musl-gcc").exists()
}
val os: org.gradle.internal.os.OperatingSystem by lazy {
org.gradle.internal.os.OperatingSystem.current()
val os: OS by lazy {
val currentOs = OperatingSystem.current()
when {
currentOs.isMacOsX -> OS.MacOS
currentOs.isLinux -> OS.Linux
currentOs.isWindows -> OS.Windows
else -> throw GradleException("Cannot build on ${currentOs.name}")
}
}
// could be `commitId: Provider<String> = project.provider { ... }`
@@ -425,3 +447,12 @@ open class BuildInfo(private val project: Project) {
// Shape of a function which is applied to configure multi-JDK testing.
private typealias MultiJdkTestConfigurator =
Test.(Pair<JavaLanguageVersion, Provider<JavaLauncher>>) -> Unit
private val OperatingSystem.canonicalName
get() =
when {
isMacOsX -> "macos"
isWindows -> "windows"
isLinux -> "linux"
else -> throw RuntimeException("Unsupported OS: $name")
}
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Target.OS
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
/**
* Task for creating static libraries from object files.
*
* Supports both Unix `ar` and Windows `lib.exe`.
*/
abstract class CArchive : DefaultTask() {
/** The object files to archive. */
@get:InputFiles abstract val objectFiles: ConfigurableFileCollection
/** The output static library file. */
@get:OutputFile abstract val outputFile: RegularFileProperty
@get:Inject protected abstract val execOperations: ExecOperations
private val buildInfo: BuildInfo = project.extensions.getByType(BuildInfo::class.java)
init {
group = "build"
}
@TaskAction
fun archive() {
outputFile.get().asFile.delete()
when (buildInfo.os) {
OS.Linux,
OS.MacOS -> archiveWithAr()
OS.Windows -> archiveWithLib()
}
logger.info("Created static library -> ${outputFile.get().asFile.name}")
}
private fun archiveWithAr() {
val output = outputFile.get().asFile
output.parentFile.mkdirs()
val args = buildList {
add("ar")
add("-rcs")
add(output.absolutePath)
objectFiles.files.forEach { file -> add(file.absolutePath) }
}
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
private fun archiveWithLib() {
val output = outputFile.get().asFile
output.parentFile.mkdirs()
val args = buildList {
add("lib.exe")
add("/OUT:${output.absolutePath}")
objectFiles.files.forEach { file -> add(file.absolutePath) }
}
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
}
+410
View File
@@ -0,0 +1,410 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Target.OS
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
/**
* Task for compiling C source files to object files or executables. Supports both GCC/Clang and
* MSVC compilers.
*/
abstract class CCompile : DefaultTask() {
/** The C source files to compile. */
@get:InputFiles abstract val sourceFiles: ConfigurableFileCollection
/** The include directories for compilation. */
@get:InputFiles abstract val includeDirs: ConfigurableFileCollection
/** Preprocessor definitions. */
@get:Input @get:Optional abstract val defines: MapProperty<String, String>
/** C standard version (e.g., "c11", "c17", "gnu11"). */
@get:Input @get:Optional abstract val cStandard: Property<String>
/** Optimization level (e.g., "0", "1", "2", "3", "s", "fast"). */
@get:Input @get:Optional abstract val optimizationLevel: Property<String>
/** Whether to include debug symbols. */
@get:Input abstract val debugSymbols: Property<Boolean>
/** Whether to generate position-independent code (GCC/Clang only). */
@get:Input abstract val positionIndependentCode: Property<Boolean>
/** Warning flags (GCC/Clang: prefixed with -W, e.g., "all", "extra"). */
@get:Input abstract val warningFlags: ListProperty<String>
/** Warning level for MSVC (0-4). */
@get:Input @get:Optional abstract val warningLevel: Property<Int>
/** Feature flags (GCC/Clang only, prefixed with -f). */
@get:Input abstract val featureFlags: ListProperty<String>
/** Machine-specific flags (GCC/Clang only, prefixed with -m). */
@get:Input abstract val machineFlags: ListProperty<String>
/** Runtime library for MSVC (e.g., "MT", "MD", "MTd", "MDd"). */
@get:Input @get:Optional abstract val runtimeLibrary: Property<String>
/** Additional compiler arguments. */
@get:Input abstract val compilerArgs: ListProperty<String>
/** The architecture to compile for (macOS only). */
@get:Input @get:Optional abstract val arch: Property<Target.Arch>
// ===== Linking properties =====
/** Whether to link into an executable (true) or just compile to object files (false). */
@get:Input abstract val link: Property<Boolean>
/** Library search paths. */
@get:InputFiles @get:Optional abstract val libraryPaths: ConfigurableFileCollection
/** Libraries to link by name. */
@get:Input @get:Optional abstract val libraries: ListProperty<String>
/** Direct library files to link. */
@get:InputFiles @get:Optional abstract val libraryFiles: ConfigurableFileCollection
/** Linker flags. */
@get:Input @get:Optional abstract val linkerFlags: ListProperty<String>
/** macOS frameworks to link (GCC/Clang only). */
@get:Input @get:Optional abstract val frameworks: ListProperty<String>
/** Output file when linking (executable name). */
@get:OutputFile @get:Optional abstract val outputFile: RegularFileProperty
/** Create a shared library (dll, dylib, so). */
@get:Input abstract val sharedLibrary: Property<Boolean>
/** The output directory for object files (when not linking). */
@get:OutputDirectory @get:Optional abstract val outputDir: DirectoryProperty
@get:Inject protected abstract val execOperations: ExecOperations
private val buildInfo: BuildInfo = project.extensions.getByType(BuildInfo::class.java)
init {
group = "build"
// Defaults
link.convention(false)
debugSymbols.convention(false)
positionIndependentCode.convention(false)
sharedLibrary.convention(false)
warningFlags.convention(listOf("all", "extra"))
warningLevel.convention(3)
featureFlags.convention(emptyList())
machineFlags.convention(emptyList())
compilerArgs.convention(emptyList())
defines.convention(emptyMap())
libraries.convention(emptyList())
linkerFlags.convention(emptyList())
frameworks.convention(emptyList())
}
@TaskAction
fun compile() {
if (link.get()) {
compileAndLink()
} else {
compileOnly()
}
}
private fun compileOnly() {
val outputDirectory = outputDir.get().asFile
outputDirectory.mkdirs()
val cFiles = sourceFiles.files.filter { it.extension == "c" }
if (cFiles.isEmpty()) {
logger.info("No C files to compile")
return
}
cFiles.forEach { cFile ->
when (buildInfo.targetMachine.os) {
OS.Linux,
OS.MacOS -> compileFileGCC(cFile, outputDirectory)
OS.Windows -> compileFileMSVC(cFile, outputDirectory)
}
}
cFiles.forEach { cFile ->
val objectFile = outputDirectory.resolve("${cFile.nameWithoutExtension}.o")
logger.info("Compiled ${cFile.name} -> ${objectFile.name}")
}
}
private fun compileFileGCC(cFile: java.io.File, outputDirectory: java.io.File) {
val objectFile = outputDirectory.resolve("${cFile.nameWithoutExtension}.o")
val args = buildList {
add("cc")
add("-c")
addAll(buildCompilerFlagsGCC())
add(cFile.absolutePath)
add("-o")
add(objectFile.absolutePath)
}
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
private fun compileFileMSVC(cFile: java.io.File, outputDirectory: java.io.File) {
val objectFile = outputDirectory.resolve("${cFile.nameWithoutExtension}.obj")
val args = buildList {
add("cl.exe")
add("/c")
addAll(buildCompilerFlagsMSVC())
add("/Fo${objectFile.absolutePath}")
add(cFile.absolutePath)
}
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
private fun compileAndLink() {
when (buildInfo.targetMachine.os) {
OS.Linux,
OS.MacOS -> compileAndLinkGCC()
OS.Windows -> compileAndLinkMSVC()
}
logger.info("Compiled and linked -> ${outputFile.get().asFile.name}")
}
private fun compileAndLinkGCC() {
val output = outputFile.get().asFile
val args = buildList {
add("cc")
addAll(buildCompilerFlagsGCC())
// Add source/object files
sourceFiles.files.forEach { file -> add(file.absolutePath) }
// Library search paths
libraryPaths.files.forEach { dir -> add("-L${dir.absolutePath}") }
// Direct library files. These must come before `-l` libraries below: GNU ld resolves
// symbols in a single left-to-right pass, so a static `-lz` etc. must appear after the
// archives (e.g. libpkl.a) whose undefined symbols it resolves, or those symbols are
// never pulled in.
libraryFiles.files.forEach { file -> add(file.absolutePath) }
// Libraries by name
libraries.get().forEach { lib -> add("-l${lib}") }
// Linker flags
if (linkerFlags.get().isNotEmpty()) {
add("-Wl,${linkerFlags.get().joinToString(",")}")
}
// macOS frameworks
if (buildInfo.os.isMacOS) {
frameworks.get().forEach { framework ->
add("-framework")
add(framework)
}
}
if (sharedLibrary.get()) {
add("-shared")
}
// Output file
add("-o")
add(output.absolutePath)
}
output.parentFile.mkdirs()
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
private fun compileAndLinkMSVC() {
val output = outputFile.get().asFile
output.parentFile.mkdirs()
val args = buildList {
add("cl.exe")
addAll(buildCompilerFlagsMSVC())
// Output executable
add("/Fe${output.absolutePath}")
// Add source/object files
sourceFiles.files.forEach { file -> add(file.absolutePath) }
// Linker section
if (
libraryPaths.files.isNotEmpty() ||
libraries.get().isNotEmpty() ||
libraryFiles.files.isNotEmpty() ||
linkerFlags.get().isNotEmpty()
) {
add("/link")
if (sharedLibrary.get()) {
add("/DLL")
}
// Library search paths
libraryPaths.files.forEach { dir -> add("/LIBPATH:${dir.absolutePath}") }
// Library files by name
libraries.get().forEach { lib -> add("lib${lib}.lib") }
// Direct library files
libraryFiles.files.forEach { file -> add(file.absolutePath) }
// Additional linker flags
addAll(linkerFlags.get())
}
}
execOperations.exec {
workingDir = project.projectDir
commandLine(args)
}
}
private fun buildCompilerFlagsGCC(): List<String> = buildList {
// Warning flags
warningFlags.get().forEach { flag -> add("-W${flag}") }
// Include directories
includeDirs.files.forEach { dir -> add("-I${dir.absolutePath}") }
// Preprocessor definitions
defines.get().forEach { (key, value) ->
if (value.isEmpty()) {
add("-D${key}")
} else {
add("-D${key}=${value}")
}
}
// C standard
if (cStandard.isPresent) {
add("-std=${cStandard.get()}")
}
// Optimization level
if (optimizationLevel.isPresent) {
add("-O${optimizationLevel.get()}")
}
// Debug symbols
if (debugSymbols.get()) {
add("-g")
}
// Position-independent code
if (positionIndependentCode.get()) {
add("-fPIC")
}
// Feature flags
featureFlags.get().forEach { flag -> add("-f${flag}") }
// Machine flags
machineFlags.get().forEach { flag -> add("-m${flag}") }
// Platform-specific flags
if (buildInfo.os.isMacOS && arch.isPresent) {
add("-arch")
add(arch.get().cCompilerName)
}
// Additional compiler arguments
addAll(compilerArgs.get())
}
private fun buildCompilerFlagsMSVC(): List<String> = buildList {
// Warning level
if (warningLevel.isPresent) {
add("/W${warningLevel.get()}")
}
// Include directories
includeDirs.files.forEach { dir -> add("/I${dir.absolutePath}") }
// Preprocessor definitions
defines.get().forEach { (key, value) ->
if (value.isEmpty()) {
add("/D${key}")
} else {
add("/D${key}=${value}")
}
}
// C standard
if (cStandard.isPresent) {
add("/std:${cStandard.get()}")
}
// Optimization level
if (optimizationLevel.isPresent) {
when (optimizationLevel.get().lowercase()) {
"0",
"d" -> add("/Od")
"1" -> add("/O1")
"2" -> add("/O2")
"x",
"fast" -> add("/Ox")
"s" -> add("/Os")
"t" -> add("/Ot")
}
}
// Debug symbols
if (debugSymbols.get()) {
add("/Zi")
}
// Runtime library
if (runtimeLibrary.isPresent) {
add("/${runtimeLibrary.get()}")
}
// Additional compiler arguments
addAll(compilerArgs.get())
}
}
+150 -22
View File
@@ -13,52 +13,80 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import javax.inject.Inject
import kotlin.io.path.createDirectories
import kotlin.io.path.exists
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.ClasspathNormalizer
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectories
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.registerIfAbsent
import org.gradle.kotlin.dsl.withNormalizer
import org.gradle.process.ExecOperations
enum class Architecture {
AMD64,
AARCH64,
}
abstract class NativeImageBuildService : BuildService<BuildServiceParameters.None>
abstract class NativeImageBuild : DefaultTask() {
@get:Input abstract val imageName: Property<String>
@get:Input abstract val outputName: Property<String>
@get:Input abstract val extraNativeImageArgs: ListProperty<String>
@get:Input abstract val arch: Property<Architecture>
@get:Input abstract val arch: Property<Target.Arch>
@get:Input abstract val mainClass: Property<String>
/**
* Names of extra directories, relative to the native-image working directory, that should be
* copied into [outputDir] after a successful build.
*/
@get:Input abstract val additionalOutputDirectoryNames: SetProperty<String>
/**
* The main class entrypoint for the executable.
*
* This option is not necessary if [sharedLibrary] is true.
*/
@get:Optional @get:Input abstract val mainClass: Property<String>
/** Create a shared library, instead of an executable. */
@get:Input abstract val sharedLibrary: Property<Boolean>
@get:InputFiles abstract val classpath: ConfigurableFileCollection
private val outputDir = project.layout.buildDirectory.dir("executable")
@get:Input abstract val envVars: MapProperty<String, String>
@get:OutputFile val outputFile = outputDir.flatMap { it.file(imageName) }
@get:InputFile @get:Optional abstract val nativeCompilerPath: RegularFileProperty
@get:Internal abstract val outputDir: DirectoryProperty
@get:Inject protected abstract val execOperations: ExecOperations
@get:Inject protected abstract val objectFactory: ObjectFactory
private val graalVm: Provider<BuildInfo.GraalVm> = arch.map { a ->
when (a) {
Architecture.AMD64 -> buildInfo.graalVmAmd64
Architecture.AARCH64 -> buildInfo.graalVmAarch64
Target.Arch.AMD64 -> buildInfo.graalVmAmd64
Target.Arch.AARCH64 -> buildInfo.graalVmAarch64
}
}
@@ -88,6 +116,10 @@ abstract class NativeImageBuild : DefaultTask() {
// CPU resources).
usesService(buildService)
sharedLibrary.convention(false)
outputDir.convention(project.layout.buildDirectory.dir("executable"))
group = "build"
inputs
@@ -98,19 +130,79 @@ abstract class NativeImageBuild : DefaultTask() {
.files(nativeImageExecutable)
.withPropertyName("graalVmNativeImage")
.withPathSensitivity(PathSensitivity.ABSOLUTE)
if (nativeCompilerPath.isPresent) {
inputs.file(nativeCompilerPath)
}
}
@TaskAction
@Suppress("unused")
@OutputFiles
fun getEffectiveOutputFiles(): FileCollection {
return objectFactory
.fileCollection()
.from(
sharedLibrary.map { isLibrary ->
val dir = outputDir.get()
val libraryName = outputName.get()
// if building a library, native-image outputs the shared library plus four headers
// (and, on Windows, an import library); otherwise, it outputs just the one file
if (isLibrary) {
val sharedLibraryExtension = buildInfo.os.sharedLibraryExtension
val staticLibraryExtension = buildInfo.os.staticLibraryExtension
dir.files(
buildList {
add("$libraryName.$sharedLibraryExtension")
if (buildInfo.os.isWindows) {
// the DLL's import library, which consumers must link against instead of
// the .dll itself
add("$libraryName.$staticLibraryExtension")
// a genuine, self-contained static archive with no runtime DLL dependency,
// produced by build_windows.bat
add("${libraryName}_s.$staticLibraryExtension")
} else {
// a genuine, self-contained static archive with no runtime dependency,
// produced by build_unix.sh
add("$libraryName.$staticLibraryExtension")
}
add("$libraryName.h")
add("${libraryName}_dynamic.h")
add("graal_isolate.h")
add("graal_isolate_dynamic.h")
}
)
} else {
val executableName = if (buildInfo.os.isWindows) "${libraryName}.exe" else libraryName
dir.file(executableName)
}
}
)
}
@Suppress("unused")
@OutputDirectories
fun getEffectiveOutputDirectories(): FileCollection =
objectFactory
.fileCollection()
.from(additionalOutputDirectoryNames.map { names -> names.map { outputDir.get().dir(it) } })
@TaskAction
protected fun run() {
execOperations.exec {
val workingDir =
project.layout.buildDirectory
.dir("tmp/native-image-build/${name}")
.get()
.asFile
.toPath()
.also { it.createDirectories() }
val execResult = execOperations.exec {
val exclusions =
listOf(buildInfo.libs.findLibrary("graalSdk").get()).map { it.get().module.name }
executable = nativeImageExecutable.get()
workingDir(outputDir)
workingDir(workingDir)
args = buildList {
add("--color=always")
// must be emitted before any experimental options are used
add("-H:+UnlockExperimentalVMOptions")
// currently gives a deprecation warning, but we've been told
@@ -118,6 +210,11 @@ abstract class NativeImageBuild : DefaultTask() {
add("--initialize-at-build-time=")
// needed for messagepack-java (see https://github.com/msgpack/msgpack-java/issues/600)
add("--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess")
// prevent storing `homeDir` in native image
add("--initialize-at-run-time=org.pkl.core.util.BaseDirectory")
add("--initialize-at-run-time=org.pkl.core.util.BaseDirectories")
// prevent storing `isEnabled`
add("--initialize-at-run-time=org.pkl.core.util.DebugLogger")
// needed for jline-terminal-jni
add("--initialize-at-run-time=org.jline.nativ,org.jline.terminal.impl.jni")
add("--no-fallback")
@@ -126,9 +223,14 @@ abstract class NativeImageBuild : DefaultTask() {
add("-H:IncludeResourceBundles=org.pkl.core.errorMessages")
add("-H:IncludeResourceBundles=org.pkl.parser.errorMessages")
add("-H:IncludeResources=org/pkl/commons/cli/PklCARoots.pem")
add("-H:Class=${mainClass.get()}")
if (mainClass.isPresent) {
add("-H:Class=${mainClass.get()}")
}
if (sharedLibrary.get()) {
add("--shared")
}
add("-o")
add(imageName.get())
add(outputName.get())
// the actual limit (currently) used by native-image is this number + 1400 (idea is to
// compensate for Truffle's own nodes)
add("-H:MaxRuntimeCompileMethods=1800")
@@ -138,10 +240,11 @@ abstract class NativeImageBuild : DefaultTask() {
// disable automatic support for JVM CLI options (puts our main class in full control of
// argument parsing)
add("-H:-ParseRuntimeOptions")
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower)
// executable
if (!buildInfo.isReleaseBuild) {
add("-Ob")
// disable all optimizations
add("-O0")
// generate debugging information
add("-g")
}
if (buildInfo.isNativeArch) {
add("-march=native")
@@ -157,8 +260,12 @@ abstract class NativeImageBuild : DefaultTask() {
// make sure dev machine stays responsive (15% slowdown on my laptop)
val processors =
Runtime.getRuntime().availableProcessors() /
if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1
if (buildInfo.os.isMacOS && !buildInfo.isCiBuild) 4 else 1
add("-J-XX:ActiveProcessorCount=${processors}")
addAll(envVars.get().entries.map { "-E${it.key}=${it.value}" })
if (nativeCompilerPath.isPresent) {
add("--native-compiler-path=${nativeCompilerPath.get().asFile}")
}
// Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed
// tools.
addAll(environment.keys.filter { it.startsWith("HOMEBREW_") }.map { "-E$it" })
@@ -166,5 +273,26 @@ abstract class NativeImageBuild : DefaultTask() {
addAll(extraArgsFromProperties)
}
}
if (execResult.exitValue == 0) {
val outputFileNames = getEffectiveOutputFiles().files.map { it.name }
for (fileName in outputFileNames) {
val sourceFile = workingDir.resolve(fileName)
val targetFile = outputDir.get().asFile.toPath().resolve(fileName)
if (!sourceFile.exists()) {
throw GradleException("Expected to find $fileName in the working dir but didn't")
}
Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING)
}
for (dirName in additionalOutputDirectoryNames.get()) {
val sourceDir = workingDir.resolve(dirName).toFile()
if (!sourceDir.exists()) {
throw GradleException("Expected to find directory $dirName in the working dir but didn't")
}
val targetDir = outputDir.get().asFile.resolve(dirName)
targetDir.deleteRecursively()
sourceDir.copyRecursively(targetDir, overwrite = true)
}
}
}
}
+15 -10
View File
@@ -61,26 +61,31 @@ fun Project.configurePklPomMetadata() {
}
}
/** Configures POM validation task to check for unresolved versions and snapshots in releases. */
fun Project.configurePomValidation() {
/**
* Configures POM validation for [publicationName] to check for unresolved versions and snapshots in
* releases.
*/
fun Project.configurePomValidation(publicationName: String = "library") {
val generatePomTaskName =
"generatePomFileFor${publicationName.replaceFirstChar { it.uppercase() }}Publication"
val generatePomTasks =
tasks.withType<GenerateMavenPom>().matching { it.name == generatePomTaskName }
val pomFiles = providers.provider { generatePomTasks.map { it.destination } }
val validatePom =
tasks.register("validatePom") {
if (tasks.findByName("generatePomFileForLibraryPublication") == null) {
return@register
}
val generatePomFileForLibraryPublication =
tasks.named<GenerateMavenPom>("generatePomFileForLibraryPublication")
val outputFile =
layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check
dependsOn(generatePomFileForLibraryPublication)
inputs.file(generatePomFileForLibraryPublication.get().destination)
dependsOn(generatePomTasks)
inputs.files(pomFiles)
outputs.file(outputFile)
onlyIf { pomFiles.get().isNotEmpty() }
doLast {
outputFile.get().asFile.delete()
val pomFile = generatePomFileForLibraryPublication.get().destination
val pomFile = pomFiles.get().single()
assert(pomFile.exists())
val text = pomFile.readText()
+110
View File
@@ -0,0 +1,110 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
/**
* A build target when building native libraries or executables.
*
* Pkl only builds for the following listed targets.
*/
enum class Target(val os: OS, val arch: Arch, val musl: Boolean) {
MacosAarch64(os = OS.MacOS, arch = Arch.AARCH64, musl = false),
LinuxAarch64(os = OS.Linux, arch = Arch.AARCH64, musl = false),
LinuxAmd64(os = OS.Linux, arch = Arch.AMD64, musl = false),
AlpineLinuxAmd64(os = OS.Linux, arch = Arch.AMD64, musl = true),
WindowsAmd64(os = OS.Windows, arch = Arch.AMD64, musl = false);
companion object {
fun from(os: OS, arch: Arch, musl: Boolean): Target {
for (target in entries) {
if (target.os == os && target.arch == arch && target.musl == musl) {
return target
}
}
throw IllegalArgumentException("Cannot build for $os-$arch with musl: $musl")
}
}
val targetName: String
get() {
return if (musl) {
assert(os == OS.Linux)
"alpine-linux-$arch"
} else "$os-$arch"
}
enum class Arch(
/** What we call this arch */
val simpleName: String,
/** What the C compiler calls this arch */
val cCompilerName: String,
) {
AARCH64("aarch64", "arm64"),
AMD64("amd64", "x86_64");
override fun toString() = simpleName
companion object {
fun fromName(name: String): Arch =
when (name) {
"aarch64" -> AARCH64
"amd64" -> AMD64
else -> throw IllegalArgumentException("Unknown arch: $name")
}
}
}
enum class OS(
val simpleName: String,
val sharedLibraryExtension: String,
val staticLibraryExtension: String,
val objectFileExtension: String,
val displayName: String,
) {
MacOS(
simpleName = "macos",
sharedLibraryExtension = "dylib",
staticLibraryExtension = "a",
objectFileExtension = "o",
displayName = "macOS",
),
Linux(
simpleName = "linux",
sharedLibraryExtension = "so",
staticLibraryExtension = "a",
objectFileExtension = "o",
displayName = "Linux",
),
Windows(
simpleName = "windows",
sharedLibraryExtension = "dll",
staticLibraryExtension = "lib",
objectFileExtension = "obj",
displayName = "Windows",
);
override fun toString(): String = simpleName
val isWindows: Boolean
get() = this == Windows
val isMacOS: Boolean
get() = this == MacOS
val isLinux: Boolean
get() = this == Linux
}
}
@@ -45,6 +45,7 @@ configurations {
tasks.withType<JavaCompile>().configureEach {
javaCompiler = buildInfo.javaCompiler
options.release = buildInfo.jvmTarget
options.compilerArgs.add("-Xlint:unchecked")
}
plugins.withType(IdeaPlugin::class).configureEach {
@@ -84,7 +84,7 @@ for ((key, value) in relocations) {
}
}
val nonRelocations = listOf("com/oracle/truffle/", "org/graalvm/", "org/jspecify")
val nonRelocations = listOf("com/oracle/truffle/", "org/graalvm/", "org/jspecify/")
tasks.shadowJar {
inputs.property("relocations", relocations)
@@ -30,7 +30,6 @@ plugins {
val executableSpec = project.extensions.getByType<ExecutableSpec>()
val buildInfo = project.extensions.getByType<BuildInfo>()
val stagedMacAmd64Executable: Configuration = configurations.create("stagedMacAmd64Executable")
val stagedMacAarch64Executable: Configuration = configurations.create("stagedMacAarch64Executable")
val stagedLinuxAmd64Executable: Configuration = configurations.create("stagedLinuxAmd64Executable")
val stagedLinuxAarch64Executable: Configuration =
@@ -54,34 +53,40 @@ val nativeImageClasspath =
val libs = the<LibrariesForLibs>()
dependencies {
fun executableFile(suffix: String) =
fun executableFile(target: Target) =
files(
layout.buildDirectory.dir("executable").map { dir ->
dir.file(executableSpec.name.map { "$it-$suffix" })
dir.file(
executableSpec.name.map { name ->
if (target.os.isWindows) "$name-${target.targetName}.exe"
else "$name-${target.targetName}"
}
)
}
)
nativeImageClasspath(libs.truffleRuntime)
nativeImageClasspath(libs.graalSdk)
stagedMacAarch64Executable(executableFile("macos-aarch64"))
stagedMacAmd64Executable(executableFile("macos-amd64"))
stagedLinuxAmd64Executable(executableFile("linux-amd64"))
stagedLinuxAarch64Executable(executableFile("linux-aarch64"))
stagedAlpineLinuxAmd64Executable(executableFile("alpine-linux-amd64"))
stagedWindowsAmd64Executable(executableFile("windows-amd64.exe"))
stagedMacAarch64Executable(executableFile(Target.MacosAarch64))
stagedLinuxAarch64Executable(executableFile(Target.LinuxAarch64))
stagedLinuxAmd64Executable(executableFile(Target.LinuxAmd64))
stagedAlpineLinuxAmd64Executable(executableFile(Target.AlpineLinuxAmd64))
stagedWindowsAmd64Executable(executableFile(Target.WindowsAmd64))
}
private fun NativeImageBuild.amd64() {
arch = Architecture.AMD64
dependsOn(":installGraalVmAmd64")
}
private fun NativeImageBuild.configure(target: Target) {
arch = target.arch
private fun NativeImageBuild.aarch64() {
arch = Architecture.AARCH64
dependsOn(":installGraalVmAarch64")
}
outputName = executableSpec.name.map { "$it-${target.targetName}" }
mainClass = executableSpec.mainClass
if (target.arch == Target.Arch.AARCH64) {
dependsOn(":installGraalVmAarch64")
} else {
dependsOn(":installGraalVmAmd64")
}
private fun NativeImageBuild.setClasspath() {
classpath.from(sourceSets.main.map { it.output })
classpath.from(
project(":pkl-commons-cli").extensions.getByType(SourceSetContainer::class)["svm"].output
@@ -89,36 +94,15 @@ private fun NativeImageBuild.setClasspath() {
classpath.from(nativeImageClasspath)
}
val macExecutableAmd64 =
tasks.register<NativeImageBuild>("macExecutableAmd64") {
imageName = executableSpec.name.map { "$it-macos-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
}
val macExecutableAarch64 =
tasks.register<NativeImageBuild>("macExecutableAarch64") {
imageName = executableSpec.name.map { "$it-macos-aarch64" }
mainClass = executableSpec.mainClass
aarch64()
setClasspath()
}
tasks.register<NativeImageBuild>("macExecutableAarch64") { configure(Target.MacosAarch64) }
val linuxExecutableAmd64 =
tasks.register<NativeImageBuild>("linuxExecutableAmd64") {
imageName = executableSpec.name.map { "$it-linux-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
}
tasks.register<NativeImageBuild>("linuxExecutableAmd64") { configure(Target.LinuxAmd64) }
val linuxExecutableAarch64 =
tasks.register<NativeImageBuild>("linuxExecutableAarch64") {
imageName = executableSpec.name.map { "$it-linux-aarch64" }
mainClass = executableSpec.mainClass
aarch64()
setClasspath()
configure(Target.LinuxAarch64)
// Ensure compatibility for kernels with page size set to 4k, 16k and 64k
// (e.g. Raspberry Pi 5, Asahi Linux)
extraNativeImageArgs.add("-H:PageSize=65536")
@@ -126,19 +110,14 @@ val linuxExecutableAarch64 =
val alpineExecutableAmd64 =
tasks.register<NativeImageBuild>("alpineExecutableAmd64") {
imageName = executableSpec.name.map { "$it-alpine-linux-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
extraNativeImageArgs.addAll(listOf("--static", "--libc=musl"))
configure(Target.AlpineLinuxAmd64)
extraNativeImageArgs.addAll("--static", "--libc=musl")
}
val windowsExecutableAmd64 =
tasks.register<NativeImageBuild>("windowsExecutableAmd64") {
imageName = executableSpec.name.map { "$it-windows-amd64" }
mainClass = executableSpec.mainClass
amd64()
setClasspath()
configure(Target.WindowsAmd64)
extraNativeImageArgs.add("-Dfile.encoding=UTF-8")
}
val assembleNative = tasks.named("assembleNative")
@@ -214,8 +193,6 @@ val testNative = tasks.named("testNative") { dependsOn(testStartNativeExecutable
val assembleNativeMacOsAarch64 =
tasks.named("assembleNativeMacOsAarch64") { wraps(macExecutableAarch64) }
val assembleNativeMacOsAmd64 = tasks.named("assembleNativeMacOsAmd64") { wraps(macExecutableAmd64) }
val assembleNativeLinuxAarch64 =
tasks.named("assembleNativeLinuxAarch64") { wraps(linuxExecutableAarch64) }
@@ -228,110 +205,51 @@ val assembleNativeAlpineLinuxAmd64 =
val assembleNativeWindowsAmd64 =
tasks.named("assembleNativeWindowsAmd64") { wraps(windowsExecutableAmd64) }
private fun MavenPublication.configurePublication(target: Target, configuration: Configuration) {
artifactId = "${executableSpec.publicationName.get()}-${target.targetName}"
pom {
name = "${executableSpec.publicationName.get()}-${target.targetName}"
url = executableSpec.website
artifact(configuration.singleFile) {
classifier = null
extension = if (target.os.isWindows) "exe" else "bin"
builtBy(configuration)
}
description =
executableSpec.documentationName.map { name ->
buildString {
append("Native $name executable for ${target.os.displayName}/${target.arch}")
if (target.musl) {
append(" and statically linked to musl")
}
append(".")
}
}
}
}
publishing {
publications {
// need to put in `afterEvaluate` because `artifactId` cannot be set lazily.
project.afterEvaluate {
create<MavenPublication>("macExecutableAmd64") {
artifactId = "${executableSpec.publicationName.get()}-macos-amd64"
artifact(stagedMacAmd64Executable.singleFile) {
classifier = null
extension = "bin"
builtBy(stagedMacAmd64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-macos-amd64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for macOS/amd64."
}
}
}
create<MavenPublication>("macExecutableAarch64") {
artifactId = "${executableSpec.publicationName.get()}-macos-aarch64"
artifact(stagedMacAarch64Executable.singleFile) {
classifier = null
extension = "bin"
builtBy(stagedMacAarch64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-macos-aarch64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for macOS/aarch64."
}
}
configurePublication(Target.MacosAarch64, stagedMacAarch64Executable)
}
create<MavenPublication>("linuxExecutableAmd64") {
artifactId = "${executableSpec.publicationName.get()}-linux-amd64"
artifact(stagedLinuxAmd64Executable.singleFile) {
classifier = null
extension = "bin"
builtBy(stagedLinuxAmd64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-linux-amd64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for linux/amd64."
}
}
configurePublication(Target.LinuxAmd64, stagedLinuxAmd64Executable)
}
create<MavenPublication>("linuxExecutableAarch64") {
artifactId = "${executableSpec.publicationName.get()}-linux-aarch64"
artifact(stagedLinuxAarch64Executable.singleFile) {
classifier = null
extension = "bin"
builtBy(stagedLinuxAarch64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-linux-aarch64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for linux/aarch64."
}
}
configurePublication(Target.LinuxAarch64, stagedLinuxAarch64Executable)
}
create<MavenPublication>("alpineLinuxExecutableAmd64") {
artifactId = "${executableSpec.publicationName.get()}-alpine-linux-amd64"
artifact(stagedAlpineLinuxAmd64Executable.singleFile) {
classifier = null
extension = "bin"
builtBy(stagedAlpineLinuxAmd64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-alpine-linux-amd64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for linux/amd64 and statically linked to musl."
}
}
configurePublication(Target.AlpineLinuxAmd64, stagedAlpineLinuxAmd64Executable)
}
create<MavenPublication>("windowsExecutableAmd64") {
artifactId = "${executableSpec.publicationName.get()}-windows-amd64"
artifact(stagedWindowsAmd64Executable.singleFile) {
classifier = null
extension = "exe"
builtBy(stagedWindowsAmd64Executable)
}
pom {
name = "${executableSpec.publicationName.get()}-windows-amd64"
url = executableSpec.website
description =
executableSpec.documentationName.map { name ->
"Native $name executable for windows/amd64."
}
}
configurePublication(Target.WindowsAmd64, stagedWindowsAmd64Executable)
}
}
}
@@ -342,7 +260,6 @@ signing {
sign(publishing.publications["linuxExecutableAarch64"])
sign(publishing.publications["linuxExecutableAmd64"])
sign(publishing.publications["macExecutableAarch64"])
sign(publishing.publications["macExecutableAmd64"])
sign(publishing.publications["alpineLinuxExecutableAmd64"])
sign(publishing.publications["windowsExecutableAmd64"])
}
@@ -15,8 +15,6 @@
*/
val assembleNativeMacOsAarch64 = tasks.register("assembleNativeMacOsAarch64") { group = "build" }
val assembleNativeMacOsAmd64 = tasks.register("assembleNativeMacOsAmd64") { group = "build" }
val assembleNativeLinuxAarch64 = tasks.register("assembleNativeLinuxAarch64") { group = "build" }
val assembleNativeLinuxAmd64 = tasks.register("assembleNativeLinuxAmd64") { group = "build" }
@@ -28,8 +26,6 @@ val assembleNativeWindowsAmd64 = tasks.register("assembleNativeWindowsAmd64") {
val testNativeMacOsAarch64 = tasks.register("testNativeMacOsAarch64") { group = "verification" }
val testNativeMacOsAmd64 = tasks.register("testNativeMacOsAmd64") { group = "verification" }
val testNativeLinuxAarch64 = tasks.register("testNativeLinuxAarch64") { group = "verification" }
val testNativeLinuxAmd64 = tasks.register("testNativeLinuxAmd64") { group = "verification" }
@@ -50,34 +46,20 @@ val assembleNative =
tasks.register("assembleNative") {
group = "build"
@Suppress("DuplicatedCode")
if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) {
throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}")
doLast {
throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}")
}
}
when {
buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> {
wraps(assembleNativeMacOsAarch64)
}
buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> {
wraps(assembleNativeMacOsAmd64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> {
wraps(assembleNativeLinuxAarch64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) wraps(assembleNativeAlpineLinuxAmd64)
else wraps(assembleNativeLinuxAmd64)
}
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
wraps(assembleNativeWindowsAmd64)
}
else -> {
doLast {
throw GradleException(
"Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}"
)
}
}
@Suppress("DuplicatedCode")
when (buildInfo.targetMachine) {
Target.MacosAarch64 -> wraps(assembleNativeMacOsAarch64)
Target.LinuxAarch64 -> wraps(assembleNativeLinuxAarch64)
Target.LinuxAmd64 -> wraps(assembleNativeLinuxAmd64)
Target.AlpineLinuxAmd64 -> wraps(assembleNativeAlpineLinuxAmd64)
Target.WindowsAmd64 -> wraps(assembleNativeWindowsAmd64)
}
}
@@ -86,34 +68,18 @@ val testNative =
group = "verification"
dependsOn(assembleNative)
@Suppress("DuplicatedCode")
if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) {
throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}")
}
when {
buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> {
dependsOn(testNativeMacOsAarch64)
}
buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> {
dependsOn(testNativeMacOsAmd64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> {
dependsOn(testNativeLinuxAarch64)
}
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) dependsOn(testNativeAlpineLinuxAmd64)
else dependsOn(testNativeLinuxAmd64)
}
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
dependsOn(testNativeWindowsAmd64)
}
else -> {
doLast {
throw GradleException(
"Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}"
)
}
}
@Suppress("DuplicatedCode")
when (buildInfo.targetMachine) {
Target.MacosAarch64 -> wraps(testNativeMacOsAarch64)
Target.LinuxAarch64 -> wraps(testNativeLinuxAarch64)
Target.LinuxAmd64 -> wraps(testNativeLinuxAmd64)
Target.AlpineLinuxAmd64 -> wraps(testNativeAlpineLinuxAmd64)
Target.WindowsAmd64 -> wraps(testNativeWindowsAmd64)
}
}
@@ -0,0 +1,15 @@
::===----------------------------------------------------------------------===//
:: Copyright © $YEAR Apple Inc. and the Pkl project authors. All rights reserved.
::
:: Licensed under the Apache License, Version 2.0 (the "License");
:: you may not use this file except in compliance with the License.
:: You may obtain a copy of the License at
::
:: https://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing, software
:: distributed under the License is distributed on an "AS IS" BASIS,
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
:: See the License for the specific language governing permissions and
:: limitations under the License.
::===----------------------------------------------------------------------===//
@@ -0,0 +1,15 @@
#===----------------------------------------------------------------------===//
# Copyright © $YEAR Apple Inc. and the Pkl project authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#===----------------------------------------------------------------------===//
+2 -2
View File
@@ -1,6 +1,6 @@
name: main
title: Main Project
version: 0.32.2
prerelease: false
version: 0.33.0-dev
prerelease: true
nav:
- nav.adoc
-8
View File
@@ -1,8 +0,0 @@
= Community
:uri-github-issue: https://github.com/apple/pkl/issues
:uri-github-discussions: https://github.com/apple/pkl/discussions
We'd love to hear from you!
* Create an {uri-github-issue}[issue]
* Ask questions on {uri-github-discussions}[GitHub Discussions]
@@ -4,6 +4,7 @@
* xref:kotlin-binding:index.adoc[Kotlin]
* xref:swift:ROOT:index.adoc[Swift]
* xref:go:ROOT:index.adoc[Go]
* xref:libpkl:index.adoc[libpkl]
* xref:bindings-specification:index.adoc[Specification]
** xref:bindings-specification:message-passing-api.adoc[Message Passing API]
** xref:bindings-specification:binary-encoding.adoc[Pkl Binary Encoding]
@@ -3,10 +3,10 @@
// the following attributes must be updated immediately before a release
// pkl version corresponding to current git commit without -dev suffix or git hash
:pkl-version-no-suffix: 0.32.2
:pkl-version-no-suffix: 0.33.0
// tells whether pkl version corresponding to current git commit
// is a release version (:is-release-version: '') or dev version (:!is-release-version:)
:is-release-version: ''
:!is-release-version:
// the remaining attributes do not need to be updated regularly
@@ -7,7 +7,10 @@ Pkl can be embedded within any host application.
The host application has access to low level controls.
It is able to manage the lifecycle of evaluators, as well as provide custom modules and resources to Pkl.
Currently, Pkl must be embedded as a child process, by shelling out to the CLI using the xref:pkl-cli:index.adoc#command-server[`pkl server`] command. In the future, a C library will also be provided.
Pkl can be embedded in one of two ways:
. By spawning xref:pkl-cli:index.adoc#command-server[`pkl server`] as a subprocess, and communicating with it via STDIN/STDOUT.
. By linking to the xref:libpkl:index.adoc[libpkl library].
When embedded, communication between a host application and Pkl happens via message passing.
The message passing specification can be found in xref:message-passing-api.adoc[].
@@ -20,7 +23,7 @@ NOTE: Pkl's Java and Kotlin libraries binds to Pkl directly, and do not use mess
A language binding for Pkl should generally have the following components:
. A client that spawns `pkl server`, and talks to it using message passing.
. A client that either spawns `pkl server` or links to `libpkl`, and talks to it using message passing.
. A deserializer that turns xref:binary-encoding.adoc[pkl binary encoding] into a structure in the host language.
. A code generator that transforms Pkl schemas into schemas written in the host language.
The code generator is mostly written in Pkl, with a lightweight executable that acts as the glue layer.
@@ -360,7 +360,7 @@ at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/e4d8c882d/stdl
<6> What Pkl evaluated to discover the error.
When Pkl prints source locations, it also prints clickable links for easy access.
For local files, it generates a link for your development environment (https://pkl-lang.org/main/current/pkl-cli/index.html#settings-file[configurable in `+~/.pkl/settings.pkl+`]).
For local files, it generates a link for your development environment (https://pkl-lang.org/main/current/pkl-cli/index.html#settings-file[configurable in `+~/.config/pkl/settings.pkl+`]).
For packages imported from elsewhere, if available, Pkl produces `https://` links to their repository.
Pkl complains about a _type constraint_.
+132
View File
@@ -0,0 +1,132 @@
= libpkl
include::ROOT:partial$component-attributes.adoc[]
libpkl is a C library for driving Pkl evaluation.
It is designed to be a low-level API that interacts with Pkl via the xref:bindings-specification:message-passing-api.adoc[message passing API].
It is meant to be paired with higher level logic written in a host language within the context of a Pkl language binding library.
The higher level logic deals with implementing the evaluator API, and serializing/deserializing messages according to the message passing API.
== Linking to libpkl
It is possible to link to libpkl either as a static library or a dynamic library.
Statically linking to libpkl means that it is possible to copy libpkl's library code directly into your own library or executable at compile time, meaning that it is self-contained.
However, this impacts the binary size of your application, as libpkl's library files are around 100MB.
On the other hand, dynamically linking to libpkl has negligible impact on binary size, but requires that end users have libpkl installed on their OS.
== Distribution
We publish archives that contain header files, as well as both shared and static libraries.
The distributions are published as releases on GitHub, and can be downloaded for the appropriate OS/architecture.
The archives contain both statically linked and dynamically linked binaries for each OS.
The archive has the following structure:
[source]
----
.
└── libpkl-<version>-<os>-<arch>
├── include
│ └── pkl.h
├── lib
│ ├── libpkl.<shared library extension>
│ ├── libpkl.<static library extension>
│ └── pkgconfig
│ ├── libpkl-static.pc
│ └── libpkl.pc
├── LICENSE.txt
├── README.md
└── THIRD-PARTY-NOTICES.txt
----
The file extensions are as follows:
|===
|OS |Static Library Extension |Shared library extension
|macOS
|`.a`
|`.dylib`
|Linux
|`.a`
|`.so`
|Windows
|`.lib`
|`.dll`
|===
=== Linking to the static vs. shared library
The simplest way to configure your compiler to link to either the static or the shared library is to use https://en.wikipedia.org/wiki/Pkg-config[pkg-config].
The libpkl library ships with two pkg-config files: `libpkl.pc`, and `libpkl-static.pc`.
The normal `libpkl.pc` links to the shared library.
To unambiguously link to the static library, use `libpkl-static.pc`.
For example, when using `cc`:
[source,shell]
----
# Link against the shared library
cc -o myprogram myprogram.c $(pkg-config --libs --cflags libpkl)
# Link against the static library
cc -o myprogram myprogram.c $(pkg-config --static --libs --cflags libpkl-static)
----
If libpkl is installed in a non-system directory, set `PKG_CONFIG_PATH` to the subpath `lib/pkgconfig` within the directory that contains libpkl. For example, given libpkl directory `/path/to/libpkl`, the environment variable should have `/path/to/libpkl/lib/pkgconfig`.
NOTE: Windows can use pkg-config through the https://www.msys2.org/docs/pkgconfig/[MSYS2 toolchain]. Using pkg-config on Windows implies compiling with gcc-style compiler like https://code.visualstudio.com/docs/cpp/config-mingw[MinGW], instead of MSVC.
[NOTE]
====
It's also possible to link to the static library with the normal `libpkl.pc`.
For example:
[source,shell]
----
cc -Wl,-Bstatic $(pkg-config --libs --cflags libpkl)
----
====
== Building libpkl from source
To build libpkl from source, the apple/pkl repository first must be cloned down to a host OS.
Build machine requirements:
* Java 21 or higher
* zlib
* C compiler
** `cc` (gcc, clang) toolchain on Unix
** MSVC toolchain on Windows
Example:
[source,shell]
----
git clone git@github.com:apple/pkl.git
cd pkl/
./gradlew libpkl:buildNative
----
This results in two directories of interest:
* `libpkl/build/distributions/`: directory containing archives (`.zip` on Windows, `.tar.gz` on macOS/Linux) of C libraries.
* `libpkl/build/native-libs/<os>-<arch>`: directory containing the built library files (the inner structure of the archive).
Cross-compilation is not supported.
== ABI compatibility
libpkl is ABI compatible across Pkl versions.
The underlying Pkl version can be accessed using the `pkl_version()` method.
+31 -20
View File
@@ -4,7 +4,6 @@ include::ROOT:partial$component-attributes.adoc[]
:uri-mise: https://mise.jdx.dev
:uri-winget: https://learn.microsoft.com/en-us/windows/package-manager/
:uri-pkl-macos-amd64-download: {uri-sonatype-snapshot-download}&a=pkl-cli-macos-amd64&e=bin
:uri-pkl-macos-aarch64-download: {uri-sonatype-snapshot-download}&a=pkl-cli-macos-aarch64&e=bin
:uri-pkl-linux-amd64-download: {uri-sonatype-snapshot-download}&a=pkl-cli-linux-amd64&e=bin
:uri-pkl-linux-aarch64-download: {uri-sonatype-snapshot-download}&a=pkl-cli-linux-aarch64&e=bin
@@ -13,7 +12,6 @@ include::ROOT:partial$component-attributes.adoc[]
:uri-pkl-java-download: {uri-sonatype-snapshot-download}&a=pkl-cli-java&e=jar
ifdef::is-release-version[]
:uri-pkl-macos-amd64-download: {github-releases}/pkl-macos-amd64
:uri-pkl-macos-aarch64-download: {github-releases}/pkl-macos-aarch64
:uri-pkl-linux-amd64-download: {github-releases}/pkl-linux-amd64
:uri-pkl-linux-aarch64-download: {github-releases}/pkl-linux-aarch64
@@ -36,7 +34,7 @@ For interactive development, the CLI includes a Read-Eval-Print Loop (REPL).
The CLI comes in multiple flavors:
* Native macOS executable for amd64 (tested on macOS 10.15)
* Native macOS executable for aarch64 (tested on macOS 10.15)
* Native Linux executable for amd64
* Native Linux executable for aarch64
* Native Alpine Linux executable for amd64 (cross-compiled and tested on Oracle Linux 8)
@@ -140,8 +138,6 @@ Development and release versions can be downloaded and installed manually.
=== macOS Executable
On aarch64:
[source,shell]
[subs="+attributes"]
----
@@ -150,16 +146,6 @@ chmod +x pkl
./pkl --version
----
On amd64:
[source,shell]
[subs="+attributes"]
----
curl -L -o pkl '{uri-pkl-macos-amd64-download}'
chmod +x pkl
./pkl --version
----
This should print something similar to:
[source]
@@ -321,7 +307,7 @@ The default output renderer for a module supports the following formats:
* `pcf`
* `plist`
* `properties`
* `textproto`
* `textproto` (deprecated in Pkl 0.33)
* `xml`
* `yaml`
@@ -684,6 +670,12 @@ Example: `--test-reporter minimal` +
Which test reporter to use for CLI output. Possible values are `spec` and `minimal`.
====
.--install
[%collapsible]
====
Install the built package into the module cache dir.
====
This command also takes <<common-options,common options>>.
[[command-project-resolve]]
@@ -1385,14 +1377,24 @@ it works as follows:
The Pkl settings file allows to customize the CLI experience.
A settings file is a Pkl module amending the `pkl.settings` standard library module.
Its default location is `~/.pkl/settings.pkl`.
Unless configured explicitly, Pkl will look in the following locations:
. `$XDG_CONFIG_HOME/pkl/settings.pkl`
. `%APPDATA%/pkl/settings.pkl` (on Windows only)
. `~/.config/pkl/settings.pkl`
. Subdirectory `pkl/settings.pkl` within one of the paths described by `$XDG_CONFIG_DIRS`
. `/etc/xdg/pkl/settings.pkl`
. `~/.pkl/settings.pkl` (legacy location used by Pkl 0.32 and lower)
To use a different settings file, set the `--settings` command line option, for example `--settings mysettings.pkl`.
To enforce default settings, use `--settings pkl:settings`.
The settings file is also honored by (and configurable through) the Gradle plugin and `CliEvaluator` API.
Here is a typical settings file:
.~/.pkl/settings.pkl
.~/.config/pkl/settings.pkl
[source%parsed,{pkl}]
----
amends "pkl:settings" // <1>
@@ -1414,10 +1416,19 @@ When making TLS requests, Pkl comes with its own set of {uri-certificates}[CA ce
These certificates can be overridden via either of the two options:
- Set them directly via the CLI option `--ca-certificates <path>`.
- Add them to a directory at path `~/.pkl/cacerts/`.
- Add them to a user directory.
If CA certificates are not explicitly configured, Pkl will look in the following locations:
. `$XDG_CONFIG_HOME/pkl/cacerts`
. `%APPDATA%/pkl/cacerts` (on Windows only)
. `~/.config/pkl/cacerts`
. Subdirectory `pkl/cacerts` within one of the paths described by `$XDG_CONFIG_DIRS`
. `/etc/xdg/pkl/cacerts`
. `~/.pkl/cacerts` (legacy location used by Pkl 0.32 and lower)
Both these options will *replace* the default CA certificates bundled with Pkl. +
The CLI option takes precedence over the certificates in `~/.pkl/cacerts/`. +
The CLI option takes precedence over the certificates in the cacerts directory. +
Certificates need to be X.509 certificates in PEM format.
[[http-proxy]]
@@ -36,9 +36,15 @@ Possible values:
.--cache-dir
[%collapsible]
====
Default: `~/.pkl/cache` +
Example: `/path/to/module/cache/` +
The cache directory for storing packages.
If unset, defaults to the following locations:
. `$XDG_CACHE_HOME/pkl`
. `$LOCALAPPDATA/pkl/Cache` (on Windows only)
. `~/.cache/pkl` (if `$XDG_CACHE_HOME` and `$LOCALAPPDATA` are both unset)
====
.--no-cache
@@ -97,7 +103,7 @@ Any symlinks are resolved before this check is performed.
Default: (none) +
Example: `mySettings.pkl` +
File path of the Pkl settings file to use.
If not set, `~/.pkl/settings.pkl` or defaults specified in the `pkl.settings` standard library module are used.
If not set, `~/.config/pkl/settings.pkl` on Unix or `%APPDATA%/pkl/settings.pkl` on Windows (or the legacy `~/.pkl/settings.pkl`), or defaults specified in the `pkl.settings` standard library module are used.
====
.-t, --timeout
+1 -4
View File
@@ -5,7 +5,6 @@ include::ROOT:partial$component-attributes.adoc[]
:uri-DocPackageInfo: {uri-pkl-stdlib-docs}/DocPackageInfo/
:uri-CliDocGenerator: {uri-pkl-doc-main-sources}/CliDocGenerator.kt
:uri-DocGenerator: {uri-pkl-doc-main-sources}/DocGenerator.kt
:uri-pkldoc-macos-amd64-download: {uri-sonatype-snapshot-download}&a=pkldoc-macos-amd64&e=bin
:uri-pkldoc-macos-aarch64-download: {uri-sonatype-snapshot-download}&a=pkldoc-macos-aarch64&e=bin
:uri-pkldoc-linux-amd64-download: {uri-sonatype-snapshot-download}&a=pkldoc-linux-amd64&e=bin
:uri-pkldoc-linux-aarch64-download: {uri-sonatype-snapshot-download}&a=pkldoc-linux-aarch64&e=bin
@@ -14,7 +13,6 @@ include::ROOT:partial$component-attributes.adoc[]
:uri-pkldoc-java-download: {uri-sonatype-snapshot-download}&a=pkldoc-java&e=jar
ifdef::is-release-version[]
:uri-pkldoc-macos-amd64-download: {github-releases}/pkldoc-macos-amd64
:uri-pkldoc-macos-aarch64-download: {github-releases}/pkldoc-macos-aarch64
:uri-pkldoc-linux-amd64-download: {github-releases}/pkldoc-linux-amd64
:uri-pkldoc-linux-aarch64-download: {github-releases}/pkldoc-linux-aarch64
@@ -172,7 +170,7 @@ endif::[]
The CLI comes in multiple flavors:
* Native macOS executable for amd64 (tested on macOS 10.15)
* Native macOS executable for aarch64 (tested on macOS 10.15)
* Native Linux executable for amd64
* Native Linux executable for aarch64
* Native Alpine Linux executable for amd64 (cross-compiled and tested on Oracle Linux 8)
@@ -192,7 +190,6 @@ However, it requires a Java 17 (or higher) runtime on the system path, and has a
Download links:
* macOS aarch64: {uri-pkldoc-macos-aarch64-download}
* macOS amd64: {uri-pkldoc-macos-amd64-download}
* Linux aarch64: {uri-pkldoc-linux-aarch64-download}
* Linux amd64: {uri-pkldoc-linux-amd64-download}
* Alpine Linux amd64: {uri-pkldoc-alpine-download}
+8 -1
View File
@@ -152,7 +152,7 @@ The default output renderer for a module supports the following formats:
* `"pcf"`
* `"plist"`
* `"properties"`
* `"textproto"`
* `"textproto"` (deprecated in Pkl 0.33)
* `"xml"`
* `"yaml"`
====
@@ -694,6 +694,13 @@ Example: `reporter = "minimal"` +
Which test reporter to use for CLI output. Possible values are `"spec"` and `"minimal"`.
====
.install: Property<Boolean>
[%collapsible]
====
Default: `false` +
Install the built package into the module cache dir.
====
Common properties:
include::../partials/gradle-common-properties.adoc[]
@@ -64,7 +64,9 @@ Default: `null` +
Example 1: `moduleCacheDir = layout.buildDirectory.dir("pkl-module-cache")` +
Example 2: `moduleCacheDir.fileValue file("/absolute/path/to/cache")` +
The cache directory for storing packages.
If `null`, defaults to `~/.pkl/cache`.
If `null`, defaults to `~/.cache/pkl` on Unix or `%LOCALAPPDATA%/pkl/Cache` on Windows.
This setting can also be configured using the `$XDG_CACHE_HOME` environment variable.
====
.color: Property<Boolean>
@@ -69,7 +69,7 @@ Example: `settingsModule = layout.projectDirectory.file("mySettings.pkl")` +
The Pkl settings module to use.
This property accepts the same input types as the `sourceModules` property.
If `null`, `~/.pkl/settings.pkl` or defaults specified in the `pkl.settings` standard library module are used.
If `null`, `~/.config/pkl/settings.pkl` on Unix or `%APPDATA%/pkl/settings.pkl` on Windows (or the legacy `~/.pkl/settings.pkl`), or defaults specified in the `pkl.settings` standard library module are used.
====
include::../partials/gradle-common-properties.adoc[]
+1 -1
View File
@@ -1,6 +1,6 @@
= Pkl 0.32 Release Notes
:version: 0.32
:version-minor: 0.32.2
:version-minor: 0.32.1
:release-date: July 8th, 2026
:version-next: 0.33
:version-next-date: October 2026
+137
View File
@@ -0,0 +1,137 @@
= Pkl 0.33 Release Notes
:version: 0.33
:version-minor: 0.33.0
:release-date: XXX
:version-next: 0.34
:version-next-date: February 2027
include::partial$intro.adoc[]
== Highlights [small]#💖#
=== XXX
== Noteworthy [small]#🎶#
=== CLI Changes
==== Default file locations
For new setups, the CLI no longer stores anything under `~/.pkl` (https://github.com/apple/pkl/pull/1809[#1809]).
It uses XDG-style locations on Unix and Known Folder locations on Windows:
[cols="1,2,2,2",options="header"]
|===
| Concern | Unix (Linux/macOS) | Windows | Legacy fallback
| Package cache
| `~/.cache/pkl`
| `$LOCALAPPDATA/pkl/Cache`
| none
| Settings file
| `~/.config/pkl/settings.pkl`
| `$APPDATA/pkl/settings.pkl`
| `~/.pkl/settings.pkl`
| CA certificates
| `~/.config/pkl/cacerts`
| `$APPDATA/pkl/cacerts`
| `~/.pkl/cacerts`
| REPL history
| `~/.local/state/pkl/repl-history`
| `$LOCALAPPDATA/pkl/repl-history`
| none
|===
On every OS, these locations can be overridden with XDG-style env vars.
For example, setting `XDG_CACHE_HOME` will configure the cache directory.
Note that the existing `~/.pkl/cache` directory is ignored, so Pkl will download packages to populate its cache if configured to do so.
== Breaking Changes [small]#💔#
=== Removal of macOS amd64 (Intel) Binaries
Native `pkl` and `pkldoc` binaries for Intel Mac systems are no longer published with Pkl releases.
To continue running new Pkl releases on these systems, use an appropriate Java runtime and the `jpkl` and `jpkldoc` Java executables.
=== Type check changes for `Class<T>`
In prior versions of Pkl, type arguments to the `Class` type were erased.
Any `Class` value would typecheck against `Class<T>` for any value of `T`.
In Pkl 0.33, this erasure has been removed.
A `Class` value typechecked against `Class<T>` must be a subclass of `T`.
If `T` does not resolve to a class type (i.e. it is a union type, nullable type, string literal type, parameterized type, or `nothing`), the type check will always fail.
=== Standard library `external` module properties are now marked `const`
Previously, module properties in `pkl:math` and `pkl:release` could be overriden by amending the module.
Doing this makes little sense—these value are constants and overriding them by amending their module would be odd.
This is a breaking change for code that looks like this:
[source,pkl]
----
bogusMath = (import("pkl:math")) {
minInt = -1
}
----
=== Changes to `join` method on `List`, `Set`, and `Listing`
The `join` method in `List`, `Set`, and `Listing` will now call the `toString()` method if overridden on an object (https://github.com/apple/pkl/pull/1872[#1872]).
This is a breaking change, but is also considered a bugfix.
The following snippet will produce different results:
[source,pkl]
----
class Bird {
name: String
function toString() = "{ My name is \(name) }"
}
hidden birds: Listing<Bird> = new {
new { name = "Osprey" }
new { name = "Eagle" }
}
res = birds.join("\n")
----
Currently, Pkl ignores the `toString()` override, and produces:
[source,pkl]
----
res = """
new Bird { name = "Osprey" }
new Bird { name = "Eagle" }
"""
----
In Pkl 0.33, the overridden `toString()` will be called, and produces:
[source,pkl]
----
res = """
{ My name is Osprey }
{ My name is Eagle }
"""
----
== Bug Fixes [small]#🐜#
* XXX
== Contributors [small]#🙏#
We would like to thank the contributors to this release (in alphabetical order):
* XXX
@@ -1,23 +1,10 @@
= Changelog
include::ROOT:partial$component-attributes.adoc[]
[[release-0.32.2]]
== 0.32.2 (2026-10-21)
[[release-0.33.0]]
== 0.33.0 (UNRELEASED)
=== Security Fixes [small]#🔒#
* Update fallback CA certificate trust and remove trust for the issuer of compromised intermediate CAs (pr:https://github.com/apple/pkl/pull/1923).
=== Fixes
* Fix rendering of stack traces for evaluated multi-line expressions (pr:https://github.com/apple/pkl/pull/1793).
=== Contributors ❤️
Thank you to all the contributors for this release!
* https://github.com/HT154[@HT154]
* https://github.com/protobufel2[@protobufel2]
xref:0.33.adoc[Release Notes]
[[release-0.32.1]]
== 0.32.1 (2026-07-23)
+2
View File
@@ -23,6 +23,7 @@
**** xref:kotlin-binding:pkl-config-kotlin.adoc[pkl-config-kotlin Library]
*** xref:swift:ROOT:index.adoc[Swift]
*** xref:go:ROOT:index.adoc[Go]
*** xref:libpkl:index.adoc[libpkl]
*** xref:bindings-specification:index.adoc[Specification]
**** xref:bindings-specification:message-passing-api.adoc[Message Passing API]
**** xref:bindings-specification:binary-encoding.adoc[Pkl Binary Encoding]
@@ -41,6 +42,7 @@
* xref:ROOT:evolution-and-roadmap.adoc[Evolution and Roadmap]
* xref:release-notes:index.adoc[Release Notes]
** xref:release-notes:0.33.adoc[0.33 Release Notes]
** xref:release-notes:0.32.adoc[0.32 Release Notes]
** xref:release-notes:0.31.adoc[0.31 Release Notes]
** xref:release-notes:0.30.adoc[0.30 Release Notes]
+1 -1
View File
@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file
group=org.pkl-lang
version=0.32.2
version=0.33.0
# google-java-format requires jdk.compiler exports
org.gradle.jvmargs= \
+26 -19
View File
@@ -5,44 +5,48 @@ checksumPlugin = "1.4.0"
clikt = "5.0.3"
commonMark = "0.29.0"
downloadTaskPlugin = "5.7.0"
eclipseCdtFormat = "12.2"
errorProne = "2.50.0"
errorPronePlugin = "5.1.0"
geantyref = "2.0.1"
#noinspection UnusedVersionCatalogEntry
googleJavaFormat = "1.35.0"
# 25.0.2 no longer supports macos-x64
graalVm = "25.0.1"
graalVm = "25.2.4"
graalVmJdkVersion = "25.0.4"
#noinspection UnusedVersionCatalogEntry
graalVmJdkVersion = "25.0.1"
graalVmInnovation = "2"
# slightly hacky but convenient place so we remember to update the checksum
# pklGraalVm.gradle.kts assumes this entry exists
#noinspection UnusedVersionCatalogEntry
graalVmSha256-macos-x64 = "a3d895b4cd1c783badbd277ec70409806bd4102fca0d2a60dbaeb0bab41aec30"
graalVmSha256-macos-x64 = "unavailable"
#noinspection UnusedVersionCatalogEntry
graalVmSha256-macos-aarch64 = "066339f24a8ab5c161548491a9400f7344e7761a1e46f8979e76c7ef11d5bc76"
graalVmSha256-macos-aarch64 = "507330fff8907de51b8621c95a21a491fa9b0d8c240de184594f40f83add3bfa"
#noinspection UnusedVersionCatalogEntry
graalVmSha256-linux-x64 = "01e39fe1a87f28b842a3e4e3b77be9b544dca3a58fa6e93b924a6106c8bac7fb"
graalVmSha256-linux-x64 = "3f4a89de8eaa96f2ed677f09957c7e872cd8467aad3537f8b5394c1b8c4b942e"
#noinspection UnusedVersionCatalogEntry
graalVmSha256-linux-aarch64 = "7aa0b9935a80e67f37c6025678393dbd123bb6f2226811decbc1a13093fc8ae2"
graalVmSha256-linux-aarch64 = "22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7"
#noinspection UnusedVersionCatalogEntry
graalVmSha256-windows-x64 = "fde83c5ceec2c75560c747ccd9f314f90e4cf5c5287416e67c4ce442e344ca27"
graalVmSha256-windows-x64 = "789d2af1c06c3c24f402d2d4a711bdbb19b36f7d8c74afe6a959492fd121ef33"
# pklGraalVm.gradle.kts assumes this entry exists
#noinspection UnusedVersionCatalogEntry
graalVmSha256-windows-aarch64 = "unavailable"
ideaExtPlugin = "1.4.1"
javaPoet = "0.16.0"
javaPoet = "0.17.0"
javaxInject = "1"
jextractPlugin = "1.+"
jimfs = "1.3.1"
jline = "4.2.1"
jline = "4.4.6"
jmh = "1.37"
jmhPlugin = "0.7.3"
jna = "5.19.1"
jspecify = "1.0.0"
junit = "6.1.0"
junit = "6.1.3"
# 1.7+ generates much more verbose code
kotlinPoet = "1.6.0"
kotlinPoet = "2.4.0"
kotlinStdLib = "2.2.21"
#noinspection UnusedVersionCatalogEntry
kotlinTarget = "2.2"
kotlinToolchain = "2.3.21"
kotlinToolchain = "2.4.0"
kotlinxHtml = "0.12.0"
# 1.9.0 is the last version compatible with Kotlin 2.2
kotlinxSerialization = "1.9.0"
@@ -56,12 +60,12 @@ msgpack = "0.9.12"
nexusPublishPlugin = "2.0.0"
nullaway = "0.13.7"
nullawayPlugin = "3.0.0"
nuValidator = "26.6.24"
nuValidator = "26.7.3"
paguro = "3.10.3"
shadowPlugin = "9.4.2"
slf4j = "2.0.18"
shadowPlugin = "9.5.1"
slf4j = "2.0.20"
snakeYaml = "3.0.1"
spotlessPlugin = "8.7.0"
spotlessPlugin = "8.8.0"
wiremock = "3.13.2"
[libraries] # ordered alphabetically
@@ -85,6 +89,8 @@ jimfs = { group = "com.google.jimfs", name = "jimfs", version.ref = "jimfs" }
jlineReader = { group = "org.jline", name = "jline-reader", version.ref = "jline" }
jlineTerminal = { group = "org.jline", name = "jline-terminal", version.ref = "jline" }
jlineTerminalJni = { group = "org.jline", name = "jline-terminal-jni", version.ref = "jline" }
jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
jnaPlatform = { group = "net.java.dev.jna", name = "jna-platform", version.ref = "jna" }
#noinspection UnusedVersionCatalogEntry
jspecify = { group = "org.jspecify", name = "jspecify", version.ref = "jspecify" }
junitApi = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
@@ -117,10 +123,10 @@ slf4jApi = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
slf4jSimple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
snakeYaml = { group = "org.snakeyaml", name = "snakeyaml-engine", version.ref = "snakeYaml" }
spotlessPlugin = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotlessPlugin"}
svm = { group = "org.graalvm.nativeimage", name = "svm", version.ref = "graalVm" }
svm = { group = "org.graalvm.nativeimage", name = "svm", version.ref = "graalVmJdkVersion" }
truffleApi = { group = "org.graalvm.truffle", name = "truffle-api", version.ref = "graalVm" }
truffleDslProcessor = { group = "org.graalvm.truffle", name = "truffle-dsl-processor", version.ref = "graalVm" }
truffleSvm = { group = "org.graalvm.nativeimage", name = "truffle-runtime-svm", version.ref = "graalVm" }
truffleSvm = { group = "org.graalvm.nativeimage", name = "truffle-runtime-svm", version.ref = "graalVmJdkVersion" }
truffleRuntime = { group = "org.graalvm.truffle", name = "truffle-runtime", version.ref = "graalVm" }
wiremock = { group = "org.wiremock", name = "wiremock", version.ref = "wiremock" }
@@ -135,3 +141,4 @@ nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "ne
#noinspection UnusedVersionCatalogEntry
nullaway = { id = "net.ltgt.nullaway", version.ref = "nullawayPlugin" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadowPlugin" }
jextract = { id = "de.infolektuell.jextract", version.ref = "jextractPlugin" }
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bbaeb2fef8710818cf0e261201dab964c572f92b942812df0c3620d62a529a01
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
+3 -3
View File
@@ -2,15 +2,15 @@
"catalogs": {},
"aliases": {
"pkl": {
"script-ref": "org.pkl-lang:pkl-cli-java:0.32.2",
"script-ref": "org.pkl-lang:pkl-cli-java:0.32.1",
"java-agents": []
},
"pkl-codegen-java": {
"script-ref": "org.pkl-lang:pkl-codegen-java:0.32.2",
"script-ref": "org.pkl-lang:pkl-codegen-java:0.32.1",
"java-agents": []
},
"pkl-codegen-kotlin": {
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.32.2",
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.32.1",
"java-agents": []
}
},
+539
View File
@@ -0,0 +1,539 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("unused")
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.kotlin.dsl.filter
plugins {
id("pklAllProjects")
id("pklGraalVm")
id("pklJavaLibrary")
id("pklNativeLifecycle")
}
val stagedMacAarch64NativeLibrary: Configuration =
configurations.create("stagedMacAarch64NativeLibrary")
val stagedLinuxAmd64NativeLibrary: Configuration =
configurations.create("stagedLinuxAmd64NativeLibrary")
val stagedLinuxAarch64NativeLibrary: Configuration =
configurations.create("stagedLinuxAarch64NativeLibrary")
val stagedAlpineLinuxAmd64NativeLibrary: Configuration =
configurations.create("stagedAlpineLinuxAmd64NativeLibrary")
val stagedWindowsAmd64NativeLibrary: Configuration =
configurations.create("stagedWindowsAmd64NativeLibrary")
val nativeTestSourceSet: SourceSet = sourceSets.create("nativeTest")
val nativeTestImplementation: Configuration =
configurations.getByName("nativeTestImplementation") {
extendsFrom(configurations.testImplementation.get())
}
val nativeTestRuntimeOnly: Configuration =
configurations.getByName("nativeTestRuntimeOnly") {
extendsFrom(configurations.testRuntimeOnly.get())
}
dependencies {
compileOnly(libs.graalSdk)
implementation(projects.pklCore)
implementation(projects.pklServer)
implementation(libs.msgpack)
implementation(libs.truffleApi)
implementation(libs.truffleRuntime)
nativeTestImplementation(projects.pklCommonsTest)
nativeTestImplementation(libs.jna)
nativeTestImplementation(libs.jnaPlatform)
nativeTestRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.withType(CCompile::class) {
if (buildInfo.targetMachine.os.isMacOS) {
arch = buildInfo.targetMachine.arch
}
}
private fun NativeImageBuild.configure(target: Target) {
arch = target.arch
if (target.arch == Target.Arch.AARCH64) {
dependsOn(":installGraalVmAarch64")
} else {
dependsOn(":installGraalVmAmd64")
}
outputDir = target.tempOutputDir
outputName = "libpkl_internal"
classpath.from(sourceSets.main.map { it.output })
classpath.from(
project(":pkl-commons-cli").extensions.getByType(SourceSetContainer::class)["svm"].output
)
classpath.from(configurations.runtimeClasspath)
sharedLibrary = true
val scriptName = if (buildInfo.os.isWindows) "build_windows.bat" else "build_unix.sh"
nativeCompilerPath = projectDir.resolve("scripts/${scriptName}")
if (!buildInfo.os.isWindows) {
// build_unix.sh extracts the object files of every archive it links against into a loose
// `objects/` dir so that `buildStaticLibrary` can merge them into a single, self-contained
// static library.
//
// Don't need to do this for Windows because it produces a merged archive.
additionalOutputDirectoryNames.add("objects")
}
}
val macNativeImageAarch64 =
tasks.register<NativeImageBuild>("macNativeImageAarch64") {
configure(Target.MacosAarch64)
// macOS only supports 16K page size
extraNativeImageArgs = buildList {
add("-H:PageSize=16384")
if (buildInfo.isCiBuild) {
add("-J-Xmx12g")
add("-H:+DeadlockWatchdogExitOnTimeout")
add("-H:DeadlockWatchdogInterval=10")
}
}
}
val linuxNativeImageAmd64 =
tasks.register<NativeImageBuild>("linuxNativeImageAmd64") { configure(Target.LinuxAmd64) }
val linuxNativeImageAarch64 =
tasks.register<NativeImageBuild>("linuxNativeImageAarch64") {
configure(Target.LinuxAarch64)
extraNativeImageArgs =
listOf(
// Ensure compatibility for kernels with page size set to 4k, 16k and 64k
// (e.g. Raspberry Pi 5, Asahi Linux)
"-H:PageSize=65536"
)
}
val alpineLinuxNativeImageAmd64 =
tasks.register<NativeImageBuild>("alpineLinuxNativeImageAmd64") {
configure(Target.AlpineLinuxAmd64)
extraNativeImageArgs.addAll("--libc=musl")
}
val windowsNativeImageAmd64 =
tasks.register<NativeImageBuild>("windowsNativeImageAmd64") {
configure(Target.WindowsAmd64)
extraNativeImageArgs.addAll("-Dfile.encoding=UTF-8", "-H:-CheckToolchain")
}
val buildNativeImageLibrary =
tasks.register("buildNativeImageLibrary") {
group = "build"
val underlyingTask =
when (buildInfo.targetMachine) {
Target.MacosAarch64 -> macNativeImageAarch64
Target.LinuxAarch64 -> linuxNativeImageAarch64
Target.LinuxAmd64 -> linuxNativeImageAmd64
Target.WindowsAmd64 -> windowsNativeImageAmd64
Target.AlpineLinuxAmd64 -> alpineLinuxNativeImageAmd64
}
dependsOn(underlyingTask)
outputs.files(underlyingTask)
}
val buildPklObjectFile =
tasks.register<CCompile>("buildPklObjectFile") {
dependsOn(buildNativeImageLibrary)
includeDirs.from(buildInfo.targetMachine.tempOutputDir)
includeDirs.from(file("src/main/c/include/"))
sourceFiles.from(file("src/main/c/pkl.c"))
positionIndependentCode = true
if (buildInfo.os.isWindows) {
// Match the dynamic CRT (ucrt/msvcrt) that native-image's bundled JDK objects expect -
// otherwise linking pkl.obj together with the merged libpkl_internal_s.lib archive hits a
// LIBCMT/MSVCRT default-lib conflict and unresolved __imp_* CRT import symbols.
runtimeLibrary = "MD"
}
outputDir =
layout.buildDirectory.dir("tmp/compileC/${name}/${buildInfo.targetMachine.targetName}")
val versionMacro =
if (buildInfo.targetMachine.os.isWindows) "\\\"${buildInfo.pklVersion}\\\""
else "\"${buildInfo.pklVersion}\""
defines.put("PKL_VERSION", versionMacro)
}
val buildStaticLibrary =
tasks.register<CArchive>("buildStaticLibrary") {
dependsOn(buildNativeImageLibrary, buildPklObjectFile)
val targetMachine = buildInfo.targetMachine
val objectExtension = targetMachine.os.objectFileExtension
val pklObjectFile = buildPklObjectFile.flatMap {
it.outputDir.map { dir -> dir.file("pkl.${objectExtension}") }
}
if (targetMachine.os.isWindows) {
// build_windows.bat produces a single self-contained merged archive
// (libpkl_internal_s.lib) directly, rather than extracting loose .obj members into an
// "objects" directory the way build_unix.sh does - pull that in instead.
objectFiles.from(
buildNativeImageLibrary.map { task ->
task.outputs.files.find {
it.name == "libpkl_internal_s.${targetMachine.os.staticLibraryExtension}"
}
}
)
} else {
objectFiles.from(
fileTree(targetMachine.tempOutputDir.get().dir("objects")).matching {
include("**/*.${objectExtension}")
}
)
}
objectFiles.from(pklObjectFile)
outputFile =
targetMachine.outputDir.map { dir ->
dir.file("lib/libpkl.${targetMachine.os.staticLibraryExtension}")
}
}
val buildSharedLibrary =
tasks.register<CCompile>("buildSharedLibrary") {
dependsOn(buildNativeImageLibrary, buildPklObjectFile)
link = true
val targetMachine = buildInfo.targetMachine
val libraryFile = buildPklObjectFile.flatMap { task ->
task.outputDir.map { dir -> dir.file("pkl.${targetMachine.os.objectFileExtension}") }
}
val extension = targetMachine.os.sharedLibraryExtension
val staticLib = buildNativeImageLibrary.map { task ->
// link against the self-contained static archive (produced by build_unix.sh /
// build_windows.bat) rather than the shared library, so libpkl doesn't carry a runtime
// dependency on a separate libpkl_internal shared library.
val fileName =
if (targetMachine.os.isWindows)
"libpkl_internal_s.${targetMachine.os.staticLibraryExtension}"
else "libpkl_internal.${targetMachine.os.staticLibraryExtension}"
task.outputs.files.find { it.name == fileName }
}
sourceFiles.from(libraryFile)
includeDirs.from(file("src/main/c/"))
libraryFiles.from(staticLib)
positionIndependentCode = true
sharedLibrary = true
outputFile = targetMachine.outputDir.map { it.file("lib/libpkl.${extension}") }
if (buildInfo.os.isMacOS) {
frameworks.addAll("Foundation", "CoreServices")
linkerFlags.addAll("-current_version", project.version.toString())
linkerFlags.addAll("-compatibility_version", "0.1.0")
// prevent JNI symbols from being exported and clobbering the symbol table
val exportedSymbolsFile = file("src/main/c/pkl.exported_symbols")
inputs.file(exportedSymbolsFile)
linkerFlags.addAll("-exported_symbols_list", exportedSymbolsFile.absolutePath)
compilerArgs.addAll("-install_name", "@rpath/libpkl.dylib")
}
if (targetMachine.os.isWindows) {
// Without an explicit /IMPLIB, MSVC would name the DLL's import library "libpkl.lib" —
// the same path buildStaticLibrary writes its static archive to.
linkerFlags.add(
targetMachine.libraryDir.map { dir -> "/IMPLIB:${dir.file("libpkl_dll.lib").asFile}" }
)
} else {
libraries.add("z")
libraries.add("pthread")
}
if (targetMachine.os.isLinux) {
libraries.add("dl")
// prevent JNI symbols from being exported and clobbering the symbol table
val versionScriptFile = file("src/main/c/pkl.map")
inputs.file(versionScriptFile)
linkerFlags.add("--version-script=${versionScriptFile.absolutePath}")
}
}
val Target.outputDir
get() = layout.buildDirectory.dir("native-libs/$targetName")
val Target.libraryDir
get() = layout.buildDirectory.dir("native-libs/$targetName/lib")
val Target.tempOutputDir
get() = layout.buildDirectory.dir("tmp/native-libs/$targetName")
val distTar =
tasks.register<Tar>("distTar") {
val isUnix = buildInfo.os.isMacOS || buildInfo.os.isLinux
onlyIf { isUnix }
dependsOn(buildSharedLibrary, buildStaticLibrary, processFiles)
compression = Compression.GZIP
val baseName = "libpkl-${buildInfo.pklVersionNonUnique}-${buildInfo.targetMachine.targetName}"
archiveFileName = "${baseName}.tar.gz"
from(buildInfo.targetMachine.outputDir)
into(baseName)
}
val distZip =
tasks.register<Zip>("distZip") {
val isWindows = buildInfo.os.isWindows
onlyIf { isWindows }
dependsOn(buildSharedLibrary, buildStaticLibrary, processFiles)
val baseName = "libpkl-${buildInfo.pklVersionNonUnique}-${buildInfo.targetMachine.targetName}"
archiveFileName = "${baseName}.zip"
from(buildInfo.targetMachine.outputDir)
into(baseName)
}
tasks.assembleNative { dependsOn(distZip, distTar, processFiles) }
val processFiles =
tasks.register<Copy>("processFiles") {
inputs.property("version", buildInfo.pklVersion)
inputs.dir("src/main/c")
inputs.dir("src/main/files")
from(fileTree("src/main/files"))
from(fileTree("src/main/c") { exclude("*.{c,map,exported_symbols}") })
includeEmptyDirs = true
into(buildInfo.targetMachine.outputDir)
val tokens = buildMap {
this["version"] = buildInfo.pklVersion
this["static_lib_filename"] = "libpkl.${buildInfo.os.staticLibraryExtension}"
this["extra_static_libs"] =
when {
buildInfo.os.isMacOS -> " -lz -framework Foundation -framework CoreServices"
buildInfo.os.isWindows -> ""
else -> " -lz"
}
this["extra_shared_libs"] =
when {
buildInfo.os.isMacOS -> " -lpkl"
buildInfo.os.isWindows -> " -l:libpkl_dll.lib"
else -> " -lpkl"
}
}
filesMatching("**/libpkl*.pc") { filter<ReplaceTokens>("tokens" to tokens) }
}
val testNativeJava =
tasks.register<Test>("testNativeJava") {
dependsOn(tasks.assembleNative)
description = "Test native libraries from Java"
group = "verification"
testClassesDirs = nativeTestSourceSet.output.classesDirs
classpath = nativeTestSourceSet.runtimeClasspath
// It's not good enough to just provide `jna.library.path` on Linux; need to also provide
// `LD_LIBRARY_PATH` or `java.library.path` so that transitive libraries can be loaded.
jvmArgumentProviders.add(
CommandLineArgumentProvider {
listOf(
"-Djna.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath,
"-Djava.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath,
"--enable-native-access=ALL-UNNAMED",
)
}
)
environment("LD_LIBRARY_PATH", buildInfo.targetMachine.libraryDir.get().asFile.absolutePath)
useJUnitPlatform()
}
// link to static library
val compileNativeTestStatic =
tasks.register<CCompile>("compileNativeTestStatic") {
description = "Build C test with static library"
group = "verification"
dependsOn(tasks.assembleNative)
val libDir = buildInfo.targetMachine.libraryDir.get().asFile
val testSrc = file("src/nativeTest/c/test_pkl.c")
link = true
sourceFiles.from(testSrc)
includeDirs.from(buildInfo.targetMachine.outputDir.map { it.file("include") })
// Match the dynamic CRT (ucrt/msvcrt) that libpkl.lib's bundled JDK objects expect -
// otherwise this defaults to the static CRT (LIBCMT), causing a defaultlib conflict and
// unresolved __imp_* CRT import symbols, same as buildPklObjectFile.
if (buildInfo.os.isWindows) {
runtimeLibrary = "MD"
}
outputFile =
layout.buildDirectory.file(
"native-test/${buildInfo.targetMachine.targetName}/test_pkl_static"
)
libraryFiles.from("${libDir.absolutePath}/libpkl.${buildInfo.os.staticLibraryExtension}")
if (!buildInfo.os.isWindows) {
libraries.add("pthread")
libraries.add("z")
}
if (buildInfo.os.isLinux) {
libraries.add("dl")
}
if (buildInfo.os.isMacOS) {
frameworks.addAll("Foundation", "CoreServices")
}
}
// link to dynamic library
val compileNativeTestDynamic =
tasks.register<CCompile>("compileNativeTestDynamic") {
description = "Build C test with dynamic library"
group = "verification"
dependsOn(tasks.assembleNative)
val testSrc = file("src/nativeTest/c/test_pkl.c")
link = true
sourceFiles.from(testSrc)
includeDirs.from(buildInfo.targetMachine.outputDir.map { it.file("include") })
outputFile =
layout.buildDirectory.file(
"native-test/${buildInfo.targetMachine.targetName}/test_pkl_dynamic"
)
libraryPaths.from(buildInfo.targetMachine.libraryDir)
if (buildInfo.os.isWindows) {
// "pkl" would resolve to libpkl.lib, which is buildStaticLibrary's static archive, not
// the DLL's import library — link against the import library directly instead.
libraryFiles.from(buildInfo.targetMachine.libraryDir.map { it.file("libpkl_dll.lib") })
} else {
libraries.add("pkl")
// Bake the library's location into the test executable so the dynamic loader can find
// libpkl.so/libpkl.dylib without needing LD_LIBRARY_PATH/DYLD_LIBRARY_PATH set at run time.
linkerFlags.add("-rpath")
linkerFlags.add(buildInfo.targetMachine.libraryDir.get().asFile.absolutePath)
}
if (buildInfo.os.isLinux) {
libraries.add("dl")
}
}
val buildNativeTestC =
tasks.register("buildNativeTestC") {
description = "Build both static and dynamic C tests"
group = "verification"
dependsOn(compileNativeTestStatic, compileNativeTestDynamic)
}
fun Task.configureDummyOutput() {
group = "verification"
// dummy output to satisfy up-to-date check
val outputFile = layout.buildDirectory.file(name)
outputs.file(outputFile)
doFirst { outputFile.get().asFile.delete() }
doLast { outputFile.get().asFile.writeText("OK") }
}
val testNativeCStatic =
tasks.register<Exec>("testNativeCStatic") {
configureDummyOutput()
group = "verification"
dependsOn(compileNativeTestStatic)
val testDir = layout.buildDirectory.dir("native-test").get().asFile
workingDir = projectDir
commandLine(compileNativeTestStatic.get().outputFile.get().asFile.absolutePath)
}
val testNativeCDynamic =
tasks.register<Exec>("testNativeCDynamic") {
configureDummyOutput()
group = "verification"
dependsOn(compileNativeTestDynamic)
val testDir = layout.buildDirectory.dir("native-test").get().asFile
workingDir = projectDir
commandLine(compileNativeTestDynamic.get().outputFile.get().asFile.absolutePath)
if (buildInfo.os.isWindows) {
// Windows has no rpath equivalent — the DLL loader only searches the exe's own
// directory and PATH, so libpkl.dll must be made findable via PATH.
doFirst {
environment(
"PATH",
"${buildInfo.targetMachine.libraryDir.get().asFile.absolutePath};${System.getenv("PATH")}",
)
}
}
}
tasks.testNative {
dependsOn(testNativeCStatic, testNativeCDynamic)
// can't run libraries from other arch in Java
if (!buildInfo.isCrossArch) {
dependsOn(testNativeJava)
}
}
spotless {
cpp {
licenseHeaderFile(
rootProject.file("build-logic/src/main/resources/license-header.star-block.txt"),
"// ",
)
target("src/*/c/**/*.c", "src/*/c/**/*.h")
eclipseCdt(libs.versions.eclipseCdtFormat.get())
}
shell {
licenseHeaderFile(
rootProject.file("build-logic/src/main/resources/license-header.hash-comment.txt"),
"## build_",
)
// skip shebang
.skipLinesMatching("^#!.+?$")
target("scripts/*.sh")
}
format("bat") {
target("scripts/*.bat")
licenseHeaderFile(
rootProject.file("build-logic/src/main/resources/license-header.batch-script.txt"),
"@echo",
)
}
}
+116
View File
@@ -0,0 +1,116 @@
#!/bin/bash
#===----------------------------------------------------------------------===//
# Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#===----------------------------------------------------------------------===//
## build_unix.sh
# Adapted from https://github.com/oracle/graal/issues/3053#issuecomment-1866735057
# This script intercepts compiler arguments from graalvm native shared images
# and additionally generates a static library.
#
# Use with --native-compiler-path=${pathToThisScript}
set -e
# Detect OS
OS=$(uname -s)
case "$OS" in
Darwin)
SHARED_LIB_EXT="dylib"
;;
Linux)
SHARED_LIB_EXT="so"
;;
*)
echo "Unsupported OS: $OS"
exit 1
;;
esac
# Determine the project name and output path based on the output library argument
for arg in "$@"; do
if [[ "$arg" == *."$SHARED_LIB_EXT" ]]; then
OUTPUT_PATH=$(dirname "$arg")
LIB_NAME=$(basename "${arg%."$SHARED_LIB_EXT"}")
break
fi
done
# Do a simple forward for any calls that are used to compile individual C files
if [[ -z $LIB_NAME ]]; then
cc "$@"
exit 0
fi
# Create a debug log in $output/logs
LOG_PATH="${OUTPUT_PATH}/logs"
LOG_FILE="${LOG_PATH}/compiler_commands.txt"
mkdir -p "$LOG_PATH"
WORKINGDIR=${PWD}
echo "Working directory: ${WORKINGDIR}" > "${LOG_FILE}"
echo "Output path: ${OUTPUT_PATH}" >> "${LOG_FILE}"
echo "Library name: ${LIB_NAME}" >> "${LOG_FILE}"
echo "OS: ${OS}" >> "${LOG_FILE}"
echo "=====================================================" >> "${LOG_FILE}"
echo " SHARED LIBRARY " >> "${LOG_FILE}"
echo "=====================================================" >> "${LOG_FILE}"
CC_ARGS=( "$@" )
echo "cc ${CC_ARGS[*]@Q}" >> "${LOG_FILE}"
cc "${CC_ARGS[@]}"
echo "=====================================================" >> "${LOG_FILE}"
echo " STATIC LIBRARY " >> "${LOG_FILE}"
echo "=====================================================" >> "${LOG_FILE}"
# To create a single static library we need to call 'ar -r' on all .o files.
# In order to also include all static library dependencies, we can first extract the
# .o files and then include them as well.
echo "======= Source archives" >> "${LOG_FILE}"
OBJECTS=${OUTPUT_PATH}/objects
rm -rf "${OBJECTS}"
mkdir "${OBJECTS}"
# Remove existing archive to avoid "fat file" errors
ARCHIVE_FILE="${OUTPUT_PATH}/${LIB_NAME}.a"
rm -f "${ARCHIVE_FILE}"
AR_ARGS="-rcs ${ARCHIVE_FILE} ${OBJECTS}/*.o"
for arg in "$@"
do
if [[ $arg =~ .*\.(a)$ ]]; then
# extract the objects (.o) of each archive (.a) into
# separate directories to avoid naming collisions
echo "$arg" >> "${LOG_FILE}"
ARCHIVE_DIR=${OBJECTS}/$(basename "${arg%.a}")
mkdir "${ARCHIVE_DIR}"
cp "$arg" "${ARCHIVE_DIR}"
cd "${ARCHIVE_DIR}" || exit
ar -x "$arg"
cd "${WORKINGDIR}" || exit
AR_ARGS+=" ${ARCHIVE_DIR}/*.o"
fi
if [[ $arg =~ .*\.(o)$ ]]; then
cp "$arg" "${OBJECTS}"
fi
done
echo "======= Objects" >> "${LOG_FILE}"
find "${OBJECTS}" -name "*.o" >> "${LOG_FILE}"
echo "======= Archive command" >> "${LOG_FILE}"
echo "ar $AR_ARGS" >> "${LOG_FILE}"
# shellcheck disable=SC2086
ar $AR_ARGS
+85
View File
@@ -0,0 +1,85 @@
::===----------------------------------------------------------------------===//
:: Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
::
:: Licensed under the Apache License, Version 2.0 (the "License");
:: you may not use this file except in compliance with the License.
:: You may obtain a copy of the License at
::
:: https://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing, software
:: distributed under the License is distributed on an "AS IS" BASIS,
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
:: See the License for the specific language governing permissions and
:: limitations under the License.
::===----------------------------------------------------------------------===//
@echo off
setlocal EnableDelayedExpansion
REM Adapted from https://github.com/oracle/graal/issues/3053#issuecomment-1866735057
REM This script intercepts compiler arguments generated by GraalVM's native-image for
REM creating a shared library, and additionally generates a self-contained static library.
REM
REM Use with --native-compiler-path=${pathToThisScript}.bat
REM Determine the library name and output path based on the .dll argument. MSVC's output-naming
REM flag is passed as `/Fe<path>` with no space between the flag and the path, so strip that
REM prefix before parsing the path - otherwise ~dp/~n misparse the combined token.
set LIB_NAME=
set OUTPUT_PATH=
for %%P in (%*) do (
echo %%P | findstr /C:".dll" 1>nul
if !errorlevel!==0 (
set "ARG=%%~P"
if "!ARG:~0,3!"=="/Fe" set "ARG=!ARG:~3!"
for %%F in ("!ARG!") do (
set LIB_NAME=%%~nF
set OUTPUT_PATH=%%~dpF
)
)
)
REM Do a simple forward for any calls that are used to compile individual C files
IF "%LIB_NAME%"=="" (
cmd /c cl %*
exit /b
)
REM Setup log path and log file
set LOG_PATH=%OUTPUT_PATH%logs
set LOG_FILE=%LOG_PATH%\compiler_commands.txt
if not exist %LOG_PATH% mkdir %LOG_PATH%
echo Working directory: %CD% > %LOG_FILE%
echo Output path: %OUTPUT_PATH% >> %LOG_FILE%
echo Library name: %LIB_NAME% >> %LOG_FILE%
echo ===================================================== >> %LOG_FILE%
echo SHARED LIBRARY >> %LOG_FILE%
echo ===================================================== >> %LOG_FILE%
set CL_ARGS=%*
echo cl.exe %CL_ARGS% >> %LOG_FILE%
cmd /c cl.exe %CL_ARGS%
echo ===================================================== >> %LOG_FILE%
echo STATIC LIBRARY >> %LOG_FILE%
echo ===================================================== >> %LOG_FILE%
REM lib.exe can combine .obj and .lib inputs directly into a single output archive, so unlike
REM build_unix.sh we don't need to manually extract members from input archives first - we just
REM collect every .obj/.lib argument that was passed to the shared-library link and re-archive
REM them together into one self-contained static library with no runtime DLL dependency.
REM We don't want to overwrite the import library needed to link against the DLL, so we append
REM "_s" to indicate that it is the static variant.
set STATIC_INPUTS=
for %%P in (%*) do (
set ARG=%%P
if /I "!ARG:~-4!"==".obj" set STATIC_INPUTS=!STATIC_INPUTS! %%P
if /I "!ARG:~-4!"==".lib" set STATIC_INPUTS=!STATIC_INPUTS! %%P
)
echo ======= Static inputs >> %LOG_FILE%
echo %STATIC_INPUTS% >> %LOG_FILE%
set LIB_ARGS=/OUT:%OUTPUT_PATH%%LIB_NAME%_s.lib %STATIC_INPUTS%
echo lib.exe %LIB_ARGS% >> %LOG_FILE%
cmd /c lib.exe %LIB_ARGS%
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// pkl.h
#ifndef PKL_H
#define PKL_H
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(_WIN32)
#define PKL_EXPORT __declspec(dllexport)
#else
#define PKL_EXPORT __attribute__((visibility("default")))
#endif
#define PKL_ERR_THREAD 1 /* Called using the same pexec_t but from a different thread */
#define PKL_ERR_PROTOCOL 2 /* Failed to decode a message */
/** Error details that occurred during a method call */
typedef struct {
char *message;
} pkl_error_t;
/**
* Pkl executor instance that manages communication with the Pkl runtime.
*
* Instances should be created via `pkl_init` and destroyed via `pkl_close`.
*
* All calls using this executor should be synchronized in the same thread.
*/
typedef struct __pkl_exec_t pkl_exec_t;
/**
* The callback that gets called when a message is received from Pkl.
*
* Messages must be deserialized to Pkl's Message Passing API:
* https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html
*
* @param[in] length The length of the message bytes
* @param[in] message The message itself
* @param[in] userData User-defined data passed in from pkl_init.
*/
typedef void (*pkl_message_response_handler)(unsigned int length, char *message,
void *userData);
/**
* Initializes and allocates a Pkl executor, writing it to the slot pointed by `exec`.
*
* To clean up resources allocated by the executor, use `pkl_close()`.
*
* All calls using this executor should come from the same thread.
*
* @param[in] handler The callback that gets called when a message is received from Pkl.
* @param[in] userData User-defined data that gets passed to handler.
* @param[out] exec The pointer to write the created pkl_exec_t to.
* @param[out] error The pointer to write error details to. Can optionally be `NULL`.
*
* @return 0 on success, non-zero on failure.
*/
PKL_EXPORT int pkl_init(pkl_message_response_handler handler, void *userData,
pkl_exec_t **exec, pkl_error_t *error);
/**
* Send a message to Pkl, providing the length and a pointer to the first byte.
*
* Messages must be serialized according to Pkl's Message Passing API:
* https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html
*
* If a message is incorrectly serialized, returns `PKL_ERR_PROTOCOL`.
* If called from a different thread than `pkl_exec_t`'s originating thread, returns
* `PKL_ERR_THREAD`.
*
* @param[in] pexec The Pkl executor instance.
* @param[in] length The length of the message, in bytes.
* @param[in] message The message to send to Pkl.
* @param[out] error The pointer to write error details to. Can optionally be `NULL`.
*
* @return 0 on success, and non-zero otherwise.
*/
PKL_EXPORT int pkl_send_message(const pkl_exec_t *pexec, unsigned int length,
char *message, pkl_error_t *error);
/**
* Cleans up any resources that were created as part of the `pkl_init` process
* for our `pkl_exec_t` instance.
*
* If called from a different thread than `pkl_exec_t`'s originating thread, returns
* `PKL_ERR_THREAD`.
*
* @param[in] pexec The Pkl executor instance.
* @param[out] error The pointer to write error details to. Can optionally be `NULL`.
*
* @return 0 on success, -1 if `pexec` is `NULL`, and an error code otherwise.
*/
PKL_EXPORT int pkl_close(pkl_exec_t *pexec, pkl_error_t *error);
/**
* Returns a null-terminated string indicating Pkl's version.
*/
PKL_EXPORT const char* pkl_version();
#if defined(__cplusplus)
}
#endif
#endif
+252
View File
@@ -0,0 +1,252 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// pkl.c
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <graal_isolate.h>
#include <libpkl_internal.h>
#include <pkl.h>
#ifndef PKL_VERSION
#define PKL_VERSION "0.0.0"
#endif
// ReSharper disable once CppClassNeverUsed
struct __pkl_exec_t {
graal_isolate_t *isolate;
graal_isolatethread_t *isolateThread;
/** The caller-supplied handler/userData from pkl_init, invoked only from `queue_thread`. */
pkl_message_response_handler handler;
void *userData;
#ifdef _WIN32
HANDLE queue_thread;
#else
pthread_t queue_thread;
#endif
};
/**
* Polls for messages coming from Pkl and sends them back to the handler.
*
* Ensures that calls into Pkl are wrapped with graal_attach_thread/graal_detach_thread, and
* calls back to the handler are _not_ inside the GraalVM thread context.
*/
#ifdef _WIN32
static DWORD WINAPI pkl_dispatch_worker(LPVOID arg) {
#else
static void* pkl_dispatch_worker(void *arg) {
#endif
const pkl_exec_t *pexec = (pkl_exec_t*) arg;
for (;;) {
graal_isolatethread_t *thread;
if (graal_attach_thread(pexec->isolate, &thread) != 0) {
fprintf(stderr,
"fatal: failed to attach response dispatch thread to isolate.\n");
abort();
}
char *message = NULL;
const int length = pkl_internal_poll_response(thread, &message);
if (graal_detach_thread(thread) != 0) {
fprintf(stderr,
"fatal: failed to detach response dispatch thread from isolate.\n");
abort();
}
if (length < 0) {
// pkl_internal_server_stop has been called and the queue is drained
break;
}
pexec->handler(length, message, pexec->userData);
// message was allocated on the Java side via UnmanagedMemory.malloc, which maps onto
// the platform's malloc/free; ownership passes to us here.
free(message);
}
#ifdef _WIN32
return 0;
#else
return NULL;
#endif
}
static int pkl_start_dispatch_worker(pkl_exec_t *pexec, pkl_error_t *error) {
#ifdef _WIN32
pexec->queue_thread = CreateThread(NULL, 0, pkl_dispatch_worker, pexec, 0, NULL);
if (pexec->queue_thread == NULL) {
#else
if (pthread_create(&pexec->queue_thread, NULL, pkl_dispatch_worker, pexec)
!= 0) {
#endif
if (error != NULL) {
error->message = "Failed to create response dispatch thread";
}
return -1;
}
return 0;
}
static void pkl_stop_dispatch_worker(pkl_exec_t *pexec) {
#ifdef _WIN32
WaitForSingleObject(pexec->queue_thread, INFINITE);
CloseHandle(pexec->queue_thread);
#else
pthread_join(pexec->queue_thread, NULL);
#endif
}
int pkl_init(pkl_message_response_handler handler, void *userData,
pkl_exec_t **exec, pkl_error_t *error) {
if (handler == NULL) {
if (error != NULL) {
error->message = "handler is null";
}
return -1;
}
if (exec == NULL) {
if (error != NULL) {
error->message = "exec is null";
}
return -1;
}
pkl_exec_t *pexec = calloc(1, sizeof(pkl_exec_t));
if (pexec == NULL) {
fprintf(stderr, "failed to allocate pkl_exec_t\n");
abort();
}
pexec->handler = handler;
pexec->userData = userData;
graal_isolate_t *isolate;
graal_isolatethread_t *isolateThread;
if (graal_create_isolate(NULL, &isolate, &isolateThread) != 0) {
if (error != NULL) {
error->message = "Failed to create graal isolate thread";
}
free(pexec);
*exec = NULL;
return -1;
}
pexec->isolate = isolate;
pexec->isolateThread = isolateThread;
if (pkl_start_dispatch_worker(pexec, error) != 0) {
graal_tear_down_isolate(isolateThread);
free(pexec);
*exec = NULL;
return -1;
}
pkl_internal_server_start(isolateThread);
*exec = pexec;
if (error != NULL) {
error->message = NULL;
}
return 0;
}
int pkl_send_message(const pkl_exec_t *pexec, const unsigned int length,
char *message, pkl_error_t *error) {
if (pexec == NULL) {
if (error != NULL) {
error->message = "pexec is null";
}
return -1;
}
if (message == NULL) {
if (error != NULL) {
error->message = "message is null";
}
return -1;
}
graal_isolatethread_t *thread = graal_get_current_thread(pexec->isolate);
if (thread != pexec->isolateThread) {
if (error != NULL) {
error->message =
"called into pkl_send_message from different thread";
}
return PKL_ERR_THREAD;
}
char *errormessage = NULL;
const int resp = pkl_internal_send_message(thread, (int) length, message,
&errormessage);
if (resp != 0) {
if (error != NULL) {
error->message = errormessage;
}
return resp;
}
if (error != NULL) {
error->message = NULL;
}
return 0;
}
int pkl_close(pkl_exec_t *pexec, pkl_error_t *error) {
if (pexec == NULL) {
if (error != NULL) {
error->message = "pexec is null";
}
return -1;
}
graal_isolatethread_t *thread = graal_get_current_thread(pexec->isolate);
if (thread != pexec->isolateThread) {
if (error != NULL) {
error->message = "called into pkl_close from different thread";
}
return PKL_ERR_THREAD;
}
// pkl_internal_server_stop unblocks queue_thread's pending/next poll call, so the join
// below is guaranteed to return; only then is it safe to tear down the isolate.
pkl_internal_server_stop(thread);
pkl_stop_dispatch_worker(pexec);
if (graal_tear_down_isolate(thread) != 0) {
fprintf(stderr, "fatal: failed to tear down graal isolate.\n");
abort();
}
free(pexec);
if (error != NULL) {
error->message = NULL;
}
return 0;
}
const char* pkl_version() {
return PKL_VERSION;
}
+4
View File
@@ -0,0 +1,4 @@
_pkl_init
_pkl_send_message
_pkl_close
_pkl_version
+9
View File
@@ -0,0 +1,9 @@
PKL {
global:
pkl_init;
pkl_send_message;
pkl_close;
pkl_version;
local:
*;
};
+1
View File
@@ -0,0 +1 @@
../../../../LICENSE.txt
+5
View File
@@ -0,0 +1,5 @@
# libpkl
libpkl is a native C library for calling into Pkl via its message passing API.
For full usage instructions, visit https://pkl-lang.org/main/current/libpkl/index.html
@@ -0,0 +1,437 @@
1) Graal Sdk (https://github.com/oracle/graal)
POM License: Universal Permissive License, Version 1.0 - http://opensource.org/licenses/UPL
Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
The Universal Permissive License (UPL), Version 1.0
Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both
(a) the Software, and
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a "Larger Work" to which the Software is contributed by such licensors),
without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms.
This license is subject to the following condition:
The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----
2) Truffle API (http://openjdk.java.net/projects/graal)
POM License: Universal Permissive License, Version 1.0 - http://opensource.org/licenses/UPL
Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
The Universal Permissive License (UPL), Version 1.0
Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both
(a) the Software, and
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a "Larger Work" to which the Software is contributed by such licensors),
without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms.
This license is subject to the following condition:
The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----
3) SubstrateVM (https://github.com/oracle/graal)
POM License: GPL 2 with Classpath Exception
The GNU General Public License (GPL)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share
and change it. By contrast, the GNU General Public License is intended to
guarantee your freedom to share and change free software--to make sure the
software is free for all its users. This General Public License applies to
most of the Free Software Foundation's software and to any other program whose
authors commit to using it. (Some other Free Software Foundation software is
covered by the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom to
distribute copies of free software (and charge for this service if you wish),
that you receive source code or can get it if you want it, that you can change
the software or use pieces of it in new free programs; and that you know you
can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny
you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must give the recipients all the rights that you have. You must
make sure that they, too, receive or can get the source code. And you must
show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy, distribute
and/or modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients to
know that what they have is not the original, so that any problems introduced
by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will
individually obtain patent licenses, in effect making the program proprietary.
To prevent this, we have made it clear that any patent must be licensed for
everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification
follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms of
this General Public License. The "Program", below, refers to any such program
or work, and a "work based on the Program" means either the Program or any
derivative work under copyright law: that is to say, a work containing the
Program or a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is included
without limitation in the term "modification".) Each licensee is addressed as
"you".
Activities other than copying, distribution and modification are not covered by
this License; they are outside its scope. The act of running the Program is
not restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by
running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as
you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the
Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may
at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus
forming a work based on the Program, and copy and distribute such modifications
or work under the terms of Section 1 above, provided that you also meet all of
these conditions:
a) You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or
in part contains or is derived from the Program or any part thereof, to be
licensed as a whole at no charge to all third parties under the terms of
this License.
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the
most ordinary way, to print or display an announcement including an
appropriate copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may redistribute
the program under these conditions, and telling the user how to view a copy
of this License. (Exception: if the Program itself is interactive but does
not normally print such an announcement, your work based on the Program is
not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be reasonably
considered independent and separate works in themselves, then this License, and
its terms, do not apply to those sections when you distribute them as separate
works. But when you distribute the same sections as part of a whole which is a
work based on the Program, the distribution of the whole must be on the terms
of this License, whose permissions for other licensees extend to the entire
whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise the
right to control the distribution of derivative or collective works based on
the Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this
License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1 and
2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to
give any third party, for a charge no more than your cost of physically
performing source distribution, a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to
distribute corresponding source code. (This alternative is allowed only
for noncommercial distribution and only if you received the program in
object code or executable form with such an offer, in accord with
Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all
the source code for all modules it contains, plus any associated interface
definition files, plus the scripts used to control compilation and installation
of the executable. However, as a special exception, the source code
distributed need not include anything that is normally distributed (in either
source or binary form) with the major components (compiler, kernel, and so on)
of the operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the source
code from the same place counts as distribution of the source code, even though
third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate
your rights under this License. However, parties who have received copies, or
rights, from you under this License will not have their licenses terminated so
long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the Program
or its derivative works. These actions are prohibited by law if you do not
accept this License. Therefore, by modifying or distributing the Program (or
any work based on the Program), you indicate your acceptance of this License to
do so, and all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program),
the recipient automatically receives a license from the original licensor to
copy, distribute or modify the Program subject to these terms and conditions.
You may not impose any further restrictions on the recipients' exercise of the
rights granted herein. You are not responsible for enforcing compliance by
third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution
of the Program by all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and
the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose that
choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original
copyright holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In
such case, this License incorporates the limitation as if written in the body
of this License.
9. The Free Software Foundation may publish revised and/or new versions of the
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems
or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any later
version", you have the option of following the terms and conditions either of
that version or of any later version published by the Free Software Foundation.
If the Program does not specify a version number of this License, you may
choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software Foundation,
write to the Free Software Foundation; we sometimes make exceptions for this.
Our decision will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible
use to the public, the best way to achieve this is to make it free software
which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach
them to the start of each source file to most effectively convey the exclusion
of warranty; and each file should have at least the "copyright" line and a
pointer to where the full notice is found.
One line to give the program's name and a brief idea of what it does.
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it
starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
software, and you are welcome to redistribute it under certain conditions;
type 'show c' for details.
The hypothetical commands 'show w' and 'show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may be
called something other than 'show w' and 'show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school,
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
'Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General Public
License instead of this License.
"CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates
are subject to the following clarification and special exception to the GPLv2,
based on the GNU Project exception for its Classpath libraries, known as the
GNU Classpath Exception, but only where Oracle has expressly included in the
particular source file's header the words "Oracle designates this particular
file as subject to the "Classpath" exception as provided by Oracle in the
LICENSE file that accompanied this code."
You should also note that Oracle includes multiple, independent programs in
this software package. Some of those programs are provided under licenses
deemed incompatible with the GPLv2 by the Free Software Foundation and others.
For example, the package includes programs licensed under the Apache License,
Version 2.0. Such programs are licensed to you under their original licenses.
Oracle facilitates your further distribution of this package by adding the
Classpath Exception to the necessary parts of its GPLv2 code, which permits you
to use that code in combination with other independent modules not licensed
under the GPLv2. However, note that this would not permit you to commingle code
under an incompatible license with Oracle's GPLv2 licensed code by, for
example, cutting and pasting such code into a file also containing Oracle's
GPLv2 licensed code and then distributing the result.
Additionally, if you were to remove the Classpath Exception from any of the
files to which it applies and distribute the result, you would likely be
required to license some or all of the other code in that distribution under
the GPLv2 as well, and since the GPLv2 is incompatible with the license terms
of some items included in the distribution by Oracle, removing the Classpath
Exception could therefore effectively compromise your ability to further
distribute the package.
Proceed with caution and we recommend that you obtain the advice of a lawyer
skilled in open source matters before removing the Classpath Exception or
making modifications to this package which may subsequently be redistributed
and/or involve the use of third party software.
CLASSPATH EXCEPTION
Linking this library statically or dynamically with other modules is making a
combined work based on this library. Thus, the terms and conditions of the GNU
General Public License version 2 cover the whole combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules, and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms and
conditions of the license of that module. An independent module is a module
which is not derived from or based on this library. If you modify this library,
you may extend this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this exception
statement from your version.
----
4) msgpack-core (https://msgpack.org/)
POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
The Apache License
Copyright © 2016 Sadayuki Furuhashi, Muga Nishizawa, Taro L. Saito, Mitsunori Komatsu, Ozawa Tsuyoshi
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License.You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
@@ -0,0 +1,11 @@
prefix=${pcfiledir}/../..
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: libpkl
Description: C library for calling into Pkl's message passing API.
URL: https://pkl-lang.org/main/current/libpkl/index.html
Version: @version@
Libs: ${libdir}/@static_lib_filename@@extra_static_libs@
Cflags: -I${includedir}
@@ -0,0 +1,11 @@
prefix=${pcfiledir}/../..
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: libpkl
Description: C library for calling into Pkl's message passing API.
URL: https://pkl-lang.org/main/current/libpkl/index.html
Version: @version@
Libs: -L${libdir}@extra_shared_libs@
Cflags: -I${includedir}
@@ -0,0 +1,112 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.UnmanagedMemory;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.word.WordFactory;
import org.jspecify.annotations.Nullable;
import org.pkl.core.messaging.MessageTransports.Logger;
import org.pkl.core.messaging.ProtocolException;
import org.pkl.server.Server;
@SuppressWarnings("unused")
public class LibPklInternal {
private static final Logger logger = new LibPklLogger();
private static final NativeTransport transport =
new NativeTransport(logger, LibPklInternal::handleSendMessageToNative);
private static @Nullable Server server;
private static final BlockingQueue<byte[]> responseQueue = new LinkedBlockingQueue<>();
// sentinel identity, not contents, marks the end of the stream - see pklInternalPollResponse
private static final byte[] STOP_SENTINEL = new byte[0];
// keep in sync with values defined in pkl.h
static final int PKL_ERR_PROTOCOL = 2;
private LibPklInternal() {}
@CEntryPoint(name = "pkl_internal_send_message")
public static int pklInternalSendMessage(
IsolateThread thread, int length, CCharPointer ptr, CCharPointerPointer errorMessage) {
try {
transport.sendMessage(length, ptr);
return 0;
} catch (ProtocolException e) {
@SuppressWarnings("resource")
var cString =
CTypeConversion.toCString("Invalid encoding: %s".formatted(e.getMessage())).get();
errorMessage.write(cString);
return PKL_ERR_PROTOCOL;
}
}
@CEntryPoint(name = "pkl_internal_server_start")
public static void pklInternalServerStart(IsolateThread thread) {
server = new Server(transport);
server.start();
}
@CEntryPoint(name = "pkl_internal_server_stop")
public static void pklInternalServerStop(IsolateThread thread) {
assert server != null;
server.close();
// wakes up (and eventually stops) whatever thread is blocked in pkl_internal_poll_response
responseQueue.add(STOP_SENTINEL);
}
/**
* Blocks until a response is available, copies it into unmanaged memory, and writes its address
* to {@code outMessage}. The caller (pkl.c) takes ownership of the returned buffer and must free
* it with the C library's {@code free}.
*
* @return the length of the response in bytes, or a negative value once the server has been
* stopped and no further responses will ever be produced.
*/
@CEntryPoint(name = "pkl_internal_poll_response")
public static int pklInternalPollResponse(IsolateThread thread, CCharPointerPointer outMessage) {
byte[] bytes;
try {
bytes = responseQueue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
outMessage.write(WordFactory.nullPointer());
return -1;
}
// identity check, not content check: a genuine zero-length message is never this instance
if (bytes == STOP_SENTINEL) {
outMessage.write(WordFactory.nullPointer());
return -1;
}
var buf = UnmanagedMemory.<CCharPointer>malloc(WordFactory.unsigned(bytes.length));
for (int i = 0; i < bytes.length; i++) {
buf.write(i, bytes[i]);
}
outMessage.write(buf);
return bytes.length;
}
public static void handleSendMessageToNative(byte[] bytes) {
responseQueue.add(bytes);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl;
import java.util.Objects;
import org.pkl.core.messaging.MessageTransports.Logger;
public class LibPklLogger implements Logger {
@Override
public void log(String msg) {
if (Objects.equals(System.getenv("PKL_DEBUG"), "1")) {
System.err.println("[libpkl] " + msg);
}
}
}
@@ -0,0 +1,46 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl;
import java.io.InputStream;
import org.graalvm.nativeimage.c.type.CCharPointer;
public class NativeInputStream extends InputStream {
private int offset;
private final int length;
private final CCharPointer ptr;
public NativeInputStream(int length, CCharPointer ptr) {
super();
this.length = length;
this.ptr = ptr;
}
@Override
public int read() {
if (available() <= 0) {
return -1;
}
var result = ptr.read(offset);
offset++;
return result & 0xFF;
}
@Override
public int available() {
return length - offset;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.function.Consumer;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.msgpack.core.MessagePack;
import org.pkl.core.PklBugException;
import org.pkl.core.messaging.Message;
import org.pkl.core.messaging.MessageTransports.AbstractMessageTransport;
import org.pkl.core.messaging.MessageTransports.Logger;
import org.pkl.core.messaging.ProtocolException;
import org.pkl.server.ServerMessagePackDecoder;
import org.pkl.server.ServerMessagePackEncoder;
public class NativeTransport extends AbstractMessageTransport {
private final Consumer<byte[]> sendMessageToNative;
protected NativeTransport(Logger logger, Consumer<byte[]> sendMessageToNative) {
super(logger);
this.sendMessageToNative = sendMessageToNative;
}
@Override
protected void doStart() {}
@Override
protected void doClose() {}
@Override
protected void doSend(Message message) throws ProtocolException {
try (var os = new ByteArrayOutputStream();
var packer = MessagePack.newDefaultPacker(os)) {
var encoder = new ServerMessagePackEncoder(packer);
encoder.encode(message);
sendMessageToNative.accept(os.toByteArray());
} catch (IOException e) {
// impossible; no IO happens during packing
throw PklBugException.unreachableCode();
} catch (ProtocolException e) {
// should never happen; messages coming from Pkl should always be well-formed.
log("Unexpected protocol exception: " + e);
throw e;
}
}
public void sendMessage(int length, CCharPointer ptr) throws ProtocolException {
if (length == 0) {
throw new ProtocolException("Unexpected end of input; 0 message bytes");
}
try (var is = new NativeInputStream(length, ptr);
var unpacker = MessagePack.newDefaultUnpacker(is)) {
var message = new ServerMessagePackDecoder(unpacker).decode();
// guaranteed by `length == 0` check above
assert message != null;
accept(message);
} catch (IOException e) {
// impossible; no IO happens during unpacking
throw PklBugException.unreachableCode();
}
}
}
@@ -0,0 +1,4 @@
@NullMarked
package org.pkl.libpkl;
import org.jspecify.annotations.NullMarked;
+105
View File
@@ -0,0 +1,105 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// test_pkl.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "pkl.h"
// Callback for receiving messages
void test_message_handler(unsigned int length, char *message, void *userData) {
printf("Received message of length %d\n", length);
// Store message in userData for verification
if (userData != NULL) {
char **stored = (char**) userData;
*stored = malloc(length + 1);
memcpy(*stored, message, length);
(*stored)[length] = '\0';
}
}
// Test pkl_version
void test_version() {
const char *version = pkl_version();
assert(version != NULL);
printf("✓ pkl_version: %s\n", version);
}
// Test pkl_init and pkl_close
void test_init_close() {
pkl_error_t err = { 0 };
pkl_exec_t *exec = NULL;
int result = pkl_init(test_message_handler, NULL, &exec, &err);
assert(result == 0);
assert(exec != NULL);
result = pkl_close(exec, &err);
assert(result == 0);
printf("✓ pkl_init/close succeeded\n");
}
// Test error handling
void test_null_send_message() {
pkl_error_t err = { 0 };
int result = pkl_send_message(NULL, 0, NULL, &err);
assert(result == -1);
result = pkl_close(NULL, &err);
assert(result == -1);
printf("✓ Error handling works\n");
}
// A zero-length message decodes to nothing and must be reported as an error, not silently accepted.
void test_empty_message() {
pkl_error_t err = { 0 };
pkl_exec_t *exec = NULL;
assert(pkl_init(test_message_handler, NULL, &exec, &err) == 0);
char dummy = 0;
int result = pkl_send_message(exec, 0, &dummy, &err);
assert(result == PKL_ERR_PROTOCOL);
assert(pkl_close(exec, &err) == 0);
printf("✓ Empty message rejected\n");
}
void test_garbage_send_message() {
pkl_error_t err = { 0 };
pkl_exec_t *exec = NULL;
assert(pkl_init(test_message_handler, NULL, &exec, &err) == 0);
char *message = (char*) (unsigned char[] ) { 0x41, 0x42, 0x43 };
int result = pkl_send_message(exec, 3, message, &err);
assert(result == PKL_ERR_PROTOCOL);
result = pkl_close(exec, &err);
assert(result == 0);
printf("✓ Garbage message becomes protocol exception\n");
}
int main() {
printf("Running libpkl C tests...\n\n");
test_version();
test_init_close();
test_null_send_message();
test_empty_message();
test_garbage_send_message();
printf("\nAll tests passed!\n");
return 0;
}
@@ -0,0 +1,52 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl
import com.sun.jna.Callback
import com.sun.jna.Library
import com.sun.jna.Native
import com.sun.jna.Pointer
import com.sun.jna.Structure
import com.sun.jna.ptr.PointerByReference
@Suppress("FunctionName")
interface LibPklJNA : Library {
companion object {
val INSTANCE: LibPklJNA = Native.load("pkl", LibPklJNA::class.java)
}
@Structure.FieldOrder("message")
class PklError : Structure() {
@JvmField var message: String? = null
}
interface PklMessageResponseHandler : Callback {
fun invoke(length: Int, message: Pointer, userData: Pointer?)
}
fun pkl_init(
handler: PklMessageResponseHandler,
userData: Pointer?,
exec: PointerByReference,
error: PklError,
): Int
fun pkl_send_message(pexec: Pointer, length: Int, message: ByteArray, error: PklError): Int
fun pkl_close(pexec: Pointer, error: PklError): Int
fun pkl_version(): String
}
@@ -0,0 +1,71 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl
import com.sun.jna.Pointer
import com.sun.jna.ptr.PointerByReference
import org.assertj.core.api.Assertions.assertThat
import org.msgpack.core.MessagePack
import org.pkl.core.messaging.Message
import org.pkl.core.messaging.MessageTransports
import org.pkl.server.ServerMessagePackDecoder
import org.pkl.server.ServerMessagePackEncoder
class LibPklMessageTransport : MessageTransports.AbstractMessageTransport({}) {
private val messageResponseHandler: LibPklJNA.PklMessageResponseHandler =
object : LibPklJNA.PklMessageResponseHandler {
override fun invoke(length: Int, message: Pointer, userData: Pointer?) {
val message = decode(message.getByteArray(0, length))
accept(message)
}
}
var pexec: Pointer? = null
override fun doStart() {
val execRef = PointerByReference()
val error = LibPklJNA.PklError()
val result = LibPklJNA.INSTANCE.pkl_init(messageResponseHandler, Pointer.NULL, execRef, error)
assertThat(result).`as` { "Failed to call pkl_init: ${error.message}" }.isEqualTo(0)
pexec = execRef.value
assertThat(pexec).isNotNull()
}
override fun doClose() {
val error = LibPklJNA.PklError()
val result = LibPklJNA.INSTANCE.pkl_close(pexec!!, error)
assertThat(result).`as` { "Failed to call pkl_close: ${error.message}" }.isEqualTo(0)
}
override fun doSend(message: Message) {
val bytes = encode(message)
val error = LibPklJNA.PklError()
val result = LibPklJNA.INSTANCE.pkl_send_message(pexec!!, bytes.size, bytes, error)
assertThat(result).`as` { "Failed to call pkl_send_message: ${error.message}" }.isEqualTo(0)
}
private fun encode(message: Message): ByteArray {
val packer = MessagePack.newDefaultBufferPacker()
val encoder = ServerMessagePackEncoder(packer)
encoder.encode(message)
return packer.toByteArray()
}
private fun decode(receivedBytes: ByteArray): Message {
val unpacker = MessagePack.newDefaultUnpacker(receivedBytes)
return ServerMessagePackDecoder(unpacker).decode()!!
}
}
@@ -0,0 +1,53 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.libpkl
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.pkl.commons.test.server.AbstractServerTest
import org.pkl.commons.test.server.TestTransport
/**
* Tests libpkl bindings by using JNA (see [LibPklJNA] and [LibPklMessageTransport]).
*
* Extends [AbstractServerTest] directly rather than
* [org.pkl.commons.test.server.AbstractHttpServerTest], since a real outbound HTTP request causes
* `doClose` to hang for about a minute here (suspected to be an unclosed `HttpClient`'s default
* thread pool blocking native isolate teardown).
*
* To run these tests in IntelliJ, add
* `-Djna.library.path=$ProjectFileDir$/libpkl/build/native-libs/<os>-<arch>/lib` to the run
* configuration.
*
* You can modify the IntelliJ JUnit configuration template so that this flag gets added
* automatically. See https://www.jetbrains.com/help/idea/run-debug-configuration.html#templates for
* more details.
*/
class LibPklServerTest : AbstractServerTest() {
override lateinit var client: TestTransport
val delegate = LibPklMessageTransport()
@BeforeEach
fun beforeEach() {
client = TestTransport(delegate).also { it.start() }
}
@AfterEach
fun afterEach() {
client.close()
}
}
@@ -0,0 +1,100 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("FunctionName")
package org.pkl.libpkl
import com.sun.jna.Pointer
import com.sun.jna.ptr.PointerByReference
import java.util.concurrent.Executors
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.core.Release
class LibPklTest {
@Test
fun `malformed message`() {
val messageResponseHandler =
object : LibPklJNA.PklMessageResponseHandler {
override fun invoke(length: Int, message: Pointer, userData: Pointer?) {}
}
val execRef = PointerByReference()
val error = LibPklJNA.PklError()
val result = LibPklJNA.INSTANCE.pkl_init(messageResponseHandler, Pointer.NULL, execRef, error)
assertThat(result).`as` { "Failed to call pkl_init: ${error.message}" }.isEqualTo(0)
val result2 =
LibPklJNA.INSTANCE.pkl_send_message(
execRef.value,
10,
byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
error,
)
assertThat(result2).isNotEqualTo(0)
assertThat(error.message).isEqualTo("Invalid encoding: Malformed message header.")
assertThat(LibPklJNA.INSTANCE.pkl_close(execRef.value, error)).isEqualTo(0)
}
@Test
fun `empty message is rejected`() {
val exec = init()
val error = LibPklJNA.PklError()
try {
val result = LibPklJNA.INSTANCE.pkl_send_message(exec, 0, byteArrayOf(0), error)
assertThat(result).isEqualTo(2)
assertThat(error.message).contains("Unexpected end of input")
} finally {
assertThat(LibPklJNA.INSTANCE.pkl_close(exec, error)).isEqualTo(0)
}
}
@Test
fun `version string matches current version`() {
val currentVersion = Release.current().version.toString()
assertThat(LibPklJNA.INSTANCE.pkl_version()).isEqualTo(currentVersion)
}
@Test
fun `calling into libpkl from different thread using same pkl_exec_t instance fails`() {
val exec = init()
val error = LibPklJNA.PklError()
val executor = Executors.newSingleThreadExecutor()
try {
executor
.submit {
val resp = LibPklJNA.INSTANCE.pkl_close(exec, error)
assertThat(resp).isEqualTo(1)
assertThat(error.message).isEqualTo("called into pkl_close from different thread")
}
.get()
} finally {
executor.shutdown()
assertThat(LibPklJNA.INSTANCE.pkl_close(exec, error)).isEqualTo(0)
}
}
private fun init(): Pointer {
val execRef = PointerByReference()
val error = LibPklJNA.PklError()
val messageResponseHandler =
object : LibPklJNA.PklMessageResponseHandler {
override fun invoke(length: Int, message: Pointer, userData: Pointer?) {}
}
assertThat(LibPklJNA.INSTANCE.pkl_init(messageResponseHandler, Pointer.NULL, execRef, error))
.`as` { "Failed to call pkl_init: ${error.message}" }
.isEqualTo(0)
return execRef.value
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ data class CliEvaluatorOptions(
* - `"pcf"` (default)
* - `"plist"`
* - `"properties"`
* - `"textproto"`
* - `"textproto"` (deprecated in Pkl 0.33)
* - `"xml"`
* - `"yaml"`
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ class CliProjectPackager(
private val testOptions: CliTestOptions,
private val outputPath: String,
private val skipPublishCheck: Boolean,
private val install: Boolean,
private val consoleWriter: Writer = System.out.writer(),
private val errWriter: Writer = System.err.writer(),
) : CliProjectCommand(baseOptions, projectDirs) {
@@ -85,6 +86,8 @@ class CliProjectPackager(
securityManager,
httpClient,
skipPublishCheck,
install,
moduleCacheDir,
consoleWriter,
)
.createPackages()
@@ -21,6 +21,7 @@ import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.groups.provideDelegate
import com.github.ajalt.clikt.parameters.options.check
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
@@ -128,6 +129,15 @@ class PackageCommand : BaseCommand(name = "package", helpLink = helpLink) {
.single()
.flag()
private val install: Boolean by
option(
names = arrayOf("--install"),
help = "Install the built package into the module cache dir",
)
.single()
.flag()
.check("not compatible with --no-cache") { !(it && baseOptions.noCache) }
override fun run() {
CliProjectPackager(
baseOptions.baseOptions(emptyList()),
@@ -135,6 +145,7 @@ class PackageCommand : BaseCommand(name = "package", helpLink = helpLink) {
testOptions.cliTestOptions,
outputPath,
skipPublishCheck,
install,
)
.run()
}
@@ -63,7 +63,11 @@ internal class Repl(workingDir: Path, private val server: ReplServer, private va
}
completer(AggregateCompleter(CommandCompleter, FileCompleter(workingDir)))
option(Option.DISABLE_EVENT_EXPANSION, true)
variable(LineReader.HISTORY_FILE, (IoUtils.getPklHomeDir().resolve("repl-history")))
// Will be null if `user.home` property is not set.
// If so, don't bother writing repl history.
IoUtils.getReplHistoryFile()?.let { historyFile ->
variable(LineReader.HISTORY_FILE, historyFile)
}
}
.build()
@@ -80,7 +84,7 @@ internal class Repl(workingDir: Path, private val server: ReplServer, private va
fun run() {
// JLine 2 history file is incompatible with JLine 3
IoUtils.getPklHomeDir().resolve("repl-history.bin").deleteIfExists()
IoUtils.getLegacyPklHomeDir().resolve("repl-history.bin").deleteIfExists()
println(ReplMessages.welcome)
println()
@@ -1286,4 +1286,41 @@ class CliCommandRunnerTest {
;;"""
)
}
@Test
fun `exec of amended subcommand`() {
writePklFile(
"sub.pkl",
"""
extends "pkl:Command"
target: String
"""
.trimIndent(),
)
val moduleUri =
writePklFile(
"cmd.pkl",
"""
amends "pkl:Command"
import "sub.pkl"
command {
name = "root"
subcommands {
new sub { target = "foo" }
}
}
"""
.trimIndent(),
)
val output = runToStdout(CliBaseOptions(sourceModules = listOf(moduleUri)), listOf("sub"))
assertThat(output)
.isEqualTo(
"""
target = "foo"
"""
.trimIndent()
)
}
}
@@ -1777,7 +1777,7 @@ result = someLib.x
PackageServer.populateCacheDir(tempDir)
val projectPath =
FileTestUtils.rootProjectDir.resolve(
"pkl-commons-cli/src/test/resources/org/pkl/commons/cli/project1/"
"pkl-commons-cli/src/main/resources/org/pkl/commons/cli/project1/"
)
val options =
CliEvaluatorOptions(
@@ -60,6 +60,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("No project visible to the working directory.")
@@ -74,6 +75,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("Directory $tempDir does not contain a PklProject file.")
@@ -96,6 +98,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err)
@@ -140,6 +143,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = buffer,
)
val err = assertThrows<CliException> { packager.run() }
@@ -187,6 +191,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = buffer,
)
packager.run()
@@ -286,6 +291,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out",
skipPublishCheck = true,
install = false,
consoleWriter = buffer,
)
packager.run()
@@ -337,6 +343,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
packager.run()
@@ -419,6 +426,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -517,6 +525,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -655,6 +664,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -695,6 +705,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -749,6 +760,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -795,6 +807,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -832,6 +845,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -875,6 +889,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -919,6 +934,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = out,
)
.run()
@@ -974,6 +990,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = false,
install = false,
consoleWriter = StringWriter(),
)
.run()
@@ -1018,6 +1035,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = false,
install = false,
consoleWriter = out,
)
.run()
@@ -1062,6 +1080,7 @@ class CliProjectPackagerTest {
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = false,
consoleWriter = StringWriter(),
)
packager.run()
@@ -1121,6 +1140,73 @@ class CliProjectPackagerTest {
)
}
@Test
fun `install package to local cache`(@TempDir tempDir: Path, @TempDir cacheDir: Path) {
tempDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir, moduleCacheDir = cacheDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = true,
consoleWriter = StringWriter(),
)
packager.run()
assertThat(cacheDir.resolve("package-2/example.com/mypackage@1.0.0/mypackage@1.0.0.json"))
.exists()
assertThat(cacheDir.resolve("package-2/example.com/mypackage@1.0.0/mypackage@1.0.0.zip"))
.exists()
}
@Test
fun `cannot install with no cache`(@TempDir tempDir: Path) {
tempDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir, noCache = true),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
install = true,
consoleWriter = StringWriter(),
)
val exc = assertThrows<CliException> { packager.run() }
assertThat(exc.message)
.isEqualTo("Cannot install package to module cache dir when module cache is disabled.")
}
private fun Path.zipFilePaths(): List<String> {
return FileSystems.newFileSystem(URI("jar:${toUri()}"), emptyMap<String, String>()).use { fs ->
Files.walk(fs.getPath("/")).map(IoUtils::toNormalizedPathString).collect(Collectors.toList())
@@ -36,6 +36,7 @@ import kotlin.let
import kotlin.takeIf
import kotlin.to
import org.pkl.commons.NameMapper
import org.pkl.commons.isValidConfigurationPropertiesPrefix
import org.pkl.core.*
import org.pkl.core.util.CodeGeneratorUtils
import org.pkl.core.util.IoUtils
@@ -540,7 +541,11 @@ class JavaCodeGenerator(
}
propertyType is PType.Class && propertyType.pClass == pClass
}
if (modulePropertiesWithMatchingType.size == 1) {
val singleProperty = modulePropertiesWithMatchingType.singleOrNull()
if (
singleProperty != null && singleProperty.simpleName.isValidConfigurationPropertiesPrefix
) {
// exactly one module property has this type -> make it available for direct injection
// (potential improvement: make type available for direct injection if it occurs exactly
// once in property tree)
@@ -751,6 +756,12 @@ class JavaCodeGenerator(
when (this) {
PType.UNKNOWN -> OBJECT.nullableIf(nullable)
PType.NOTHING -> TypeName.VOID
PType.MODULE,
PType.THIS ->
// TODO: support self types: `class Foo<T extends Foo<T>>`
throw JavaCodeGeneratorException(
"Pkl `${this}` types are not supported by the Java code generator."
)
is PType.StringLiteral -> STRING.nullableIf(nullable)
is PType.Class -> {
// if in doubt, spell it out
@@ -1555,6 +1555,56 @@ class JavaCodeGeneratorTest {
assertThat(javaCodeWithoutSpringAnnotations).compilesSuccessfully()
}
@Test
fun `spring boot -- single module property has type but invalid prefix`() {
val javaCode =
generateJavaCode(
"""
module my.mod
fooBar: FooBar
class FooBar
"""
.trimIndent(),
JavaCodeGeneratorOptions(generateSpringBootConfig = true),
)
// "fooBar" is not a valid prefix for `@ConfigurationProperties` annotations, so no
// annotation is added
assertThat(javaCode)
.contains(
"""
|
| public static final class FooBar {
"""
.trimMargin()
)
}
@Test
fun `spring boot -- single module property has type and valid prefix`() {
val javaCode =
generateJavaCode(
"""
module my.mod
server: Server
class Server
"""
.trimIndent(),
JavaCodeGeneratorOptions(generateSpringBootConfig = true),
)
assertThat(javaCode)
.contains(
"""
| @ConfigurationProperties("server")
| public static final class Server {
"""
.trimMargin()
)
}
@Test
fun `import module`() {
val library =
@@ -2261,6 +2311,24 @@ class JavaCodeGeneratorTest {
)
}
@Test
fun `block comment escaping in doc comments`() {
val javaCode =
generateJavaCode(
"""
module my.mod
class Person {
/// This doc coment needs */ escaping
foo: Int
}
"""
.trimIndent(),
JavaCodeGeneratorOptions(generateJavadoc = true),
)
assertThat(javaCode).compilesSuccessfully().contains("*&#47;")
}
private fun Map<String, String>.validateContents(
@Suppress("RemoveRedundantQualifierName") vararg assertions: kotlin.Pair<String, List<String>>
) {
@@ -22,6 +22,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import java.io.StringWriter
import java.util.*
import org.pkl.commons.NameMapper
import org.pkl.commons.isValidConfigurationPropertiesPrefix
import org.pkl.core.*
import org.pkl.core.util.CodeGeneratorUtils
import org.pkl.core.util.IoUtils
@@ -437,7 +438,10 @@ class KotlinCodeGenerator(
}
propertyType is PType.Class && propertyType.pClass == pClass
}
if (modulePropertiesWithMatchingType.size == 1) {
val singleProperty = modulePropertiesWithMatchingType.singleOrNull()
if (
singleProperty != null && singleProperty.simpleName.isValidConfigurationPropertiesPrefix
) {
// exactly one module property has this type -> make it available for direct injection
// (potential improvement: make type available for direct injection if it occurs exactly
// once in property tree)
@@ -609,7 +613,7 @@ class KotlinCodeGenerator(
// do the minimum work necessary to avoid kotlin compile errors
// generating idiomatic KDoc would require parsing doc comments, converting member links, etc.
private fun renderAsKdoc(docComment: String): String = docComment
private fun renderAsKdoc(docComment: String): String = docComment.replace("*/", "*&#47;")
private fun PClass.toKotlinPoetName(): ClassName {
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
@@ -649,6 +653,12 @@ class KotlinCodeGenerator(
when (this) {
PType.UNKNOWN -> ANY_NULL
PType.NOTHING -> NOTHING
PType.MODULE,
PType.THIS ->
// TODO: support self types: `class Foo<T extends Foo<T>>`
throw KotlinCodeGeneratorException(
"Pkl `${this}` types are not supported by the Kotlin code generator."
)
is PType.StringLiteral -> STRING
is PType.Class -> {
// if in doubt, spell it out
@@ -1470,6 +1470,56 @@ class KotlinCodeGeneratorTest {
assertThat(kotlinCodeWithoutSpringAnnotations).compilesSuccessfully()
}
@Test
fun `spring boot -- single module property has type but invalid prefix`() {
val javaCode =
generateKotlinCode(
"""
module my.mod
fooBar: FooBar
class FooBar
"""
.trimIndent(),
generateSpringBootConfig = true,
)
// "fooBar" is not a valid prefix for `@ConfigurationProperties` annotations, so no
// annotation is added
assertThat(javaCode)
.contains(
"""
|
| data class FooBar
"""
.trimMargin()
)
}
@Test
fun `spring boot -- single module property has type and valid prefix`() {
val javaCode =
generateKotlinCode(
"""
module my.mod
server: Server
class Server
"""
.trimIndent(),
generateSpringBootConfig = true,
)
assertThat(javaCode)
.contains(
"""
| @ConfigurationProperties("server")
| data class Server
"""
.trimMargin()
)
}
@Test
fun `import module`() {
val library =
@@ -2054,6 +2104,23 @@ class KotlinCodeGeneratorTest {
)
}
@Test
fun `block comment escaping in doc comments`() {
val kotlinCode =
generateKotlinCode(
"""
class Person {
/// This doc coment needs */ escaping
foo: Int
}
"""
.trimIndent(),
generateKdoc = true,
)
assertThat(kotlinCode).compilesSuccessfully().contains("*&#47;")
}
private fun Map<String, String>.validateContents(
@Suppress("RemoveRedundantQualifierName") vararg assertions: kotlin.Pair<String, List<String>>
) {
@@ -77,8 +77,9 @@ data class CliBaseOptions(
/**
* The Pkl settings file to use. A settings file is a Pkl module amending the `pkl.settings`
* standard library module. If `null`, `~/.pkl/settings.pkl` (if present) or the defaults
* specified in the `pkl:settings` standard library module are used.
* standard library module. If `null`, `~/.config/pkl/settings.pkl` (falling back to the legacy
* `~/.pkl/settings.pkl`), or the defaults specified in the `pkl:settings` standard library
* module, are used.
*/
private val settings: URI? = null,
@@ -130,8 +131,9 @@ data class CliBaseOptions(
* The given files must contain [X.509](https://en.wikipedia.org/wiki/X.509) certificates in PEM
* format.
*
* If [caCertificates] is the empty list, the certificate files in `~/.pkl/cacerts/` are used. If
* `~/.pkl/cacerts/` does not exist or is empty, Pkl's built-in CA certificates are used.
* If [caCertificates] is the empty list, the certificate files in `~/.config/pkl/cacerts/` (or
* the legacy `~/.pkl/cacerts/`) are used. If that directory does not exist or is empty, Pkl's
* built-in CA certificates are used.
*/
val caCertificates: List<Path> = listOf(),
@@ -20,6 +20,7 @@ import java.net.URI
import java.nio.file.Files
import java.nio.file.Path
import java.util.regex.Pattern
import kotlin.io.path.absolutePathString
import kotlin.io.path.isRegularFile
import org.pkl.core.*
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
@@ -33,6 +34,7 @@ import org.pkl.core.project.Project
import org.pkl.core.resource.ResourceReader
import org.pkl.core.resource.ResourceReaders
import org.pkl.core.settings.PklSettings
import org.pkl.core.util.DebugLogger
import org.pkl.core.util.IoUtils
/** Building block for CLI commands. Configured programmatically to allow for embedding. */
@@ -69,7 +71,7 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
if (cliOptions.normalizedSettingsModule != null) {
PklSettings.load(ModuleSource.uri(cliOptions.normalizedSettingsModule))
} else {
PklSettings.loadFromPklHomeDir()
PklSettings.loadFromSystem()
}
} catch (e: PklException) {
// do not use `errorRenderer` because it depends on `settings`
@@ -146,7 +148,7 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
?: evaluatorSettings?.let { settings ->
if (settings.noCache == true) null else settings.moduleCacheDir
}
?: IoUtils.getDefaultModuleCacheDir()
?: IoUtils.getSystemModuleCacheDir()
}
protected val modulePath: List<Path> by lazy {
@@ -215,17 +217,20 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
}
private fun HttpClient.Builder.addDefaultCliCertificates() {
val caCertsDir = IoUtils.getPklHomeDir().resolve("cacerts")
val caCertsDir = IoUtils.getSystemCaCertsDir()
var certsAdded = false
if (Files.isDirectory(caCertsDir)) {
Files.list(caCertsDir)
.filter { it.isRegularFile() }
.filter { it.isRegularFile() && !it.fileName.toString().startsWith(".") }
.forEach { cert ->
certsAdded = true
addCertificates(cert)
}
}
if (!certsAdded) {
if (certsAdded) {
DebugLogger.log("Loading CA certificates from ${caCertsDir.normalize().absolutePathString()}")
} else {
DebugLogger.log("Using built-in CA certificates")
val defaultCerts =
this@CliCommand.javaClass.classLoader.getResourceAsStream(
"org/pkl/commons/cli/PklCARoots.pem"
@@ -102,17 +102,7 @@ class BaseOptions : OptionGroup() {
private val defaults = CliBaseOptions()
private val output =
arrayOf(
"json",
"jsonnet",
"pcf",
"properties",
"plist",
"textproto",
"xml",
"yaml",
"pkl-binary",
)
arrayOf("json", "jsonnet", "pcf", "properties", "plist", "xml", "yaml", "pkl-binary")
val allowedModules: List<Pattern> by
option(
File diff suppressed because it is too large. Load diff
@@ -144,7 +144,7 @@ class CliCommandTest {
assertThat(cliTest.myRootDir).isNull()
assertThat(builder.environmentVariables).isEqualTo(System.getenv())
assertThat(builder.externalProperties).isEmpty()
assertThat(builder.moduleCacheDir).isEqualTo(IoUtils.getDefaultModuleCacheDir())
assertThat(builder.moduleCacheDir).isEqualTo(IoUtils.getSystemModuleCacheDir())
assertThat(cliTest.myModulePath).isEmpty()
assertThat(builder.color).isFalse
assertThat(cliTest.myProxyAddress).isNull()
@@ -26,7 +26,10 @@ dependencies {
api(libs.junitApi)
api(libs.junitEngine)
api(libs.junitParams)
api(libs.wiremock)
api(projects.pklCommons) // for convenience
implementation(projects.pklCore)
implementation(projects.pklServer)
implementation(libs.assertj)
implementation(libs.msgpack)
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,6 @@ object Executables {
object pkl : ExecutablePaths("pkl-cli") {
val macAarch64: Path = executable("pkl-macos-aarch64")
val macAmd64: Path = executable("pkl-macos-amd64")
val linuxAarch64: Path = executable("pkl-linux-aarch64")
val linuxAmd64: Path = executable("pkl-linux-amd64")
val alpineAmd64: Path = executable("pkl-alpine-linux-amd64")
@@ -65,12 +64,11 @@ object Executables {
// order (aarch64 before amd64, linux before alpine) affects [firstExisting]
override val allNative: List<Path> =
listOf(macAarch64, macAmd64, linuxAarch64, linuxAmd64, alpineAmd64, windowsAmd64)
listOf(macAarch64, linuxAarch64, linuxAmd64, alpineAmd64, windowsAmd64)
}
object pkldoc : ExecutablePaths("pkl-doc") {
val macAarch64: Path = executable("pkldoc-macos-aarch64")
val macAmd64: Path = executable("pkldoc-macos-amd64")
val linuxAarch64: Path = executable("pkldoc-linux-aarch64")
val linuxAmd64: Path = executable("pkldoc-linux-amd64")
val alpineAmd64: Path = executable("pkldoc-alpine-linux-amd64")
@@ -80,6 +78,6 @@ object Executables {
// order (aarch64 before amd64, linux before alpine) affects [firstExisting]
override val allNative: List<Path> =
listOf(macAarch64, macAmd64, linuxAarch64, linuxAmd64, alpineAmd64, windowsAmd64)
listOf(macAarch64, linuxAarch64, linuxAmd64, alpineAmd64, windowsAmd64)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -179,3 +179,6 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
currIndent.setLength(currIndent.length - indent.length)
}
}
val ByteArray.debugRendering: String
get() = MessagePackDebugRenderer(this).output
@@ -0,0 +1,74 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.commons.test.server
import com.github.tomakehurst.wiremock.client.WireMock.equalTo
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.ok
import com.github.tomakehurst.wiremock.client.WireMock.stubFor
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import com.github.tomakehurst.wiremock.client.WireMock.verify
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import java.net.URI
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.pkl.server.EvaluateRequest
import org.pkl.server.EvaluateResponse
import org.pkl.server.Http
/**
* Extends [AbstractServerTest] with HTTP tests backed by a WireMock server.
*
* Some transports can't run these at all (e.g. `LibPklServerTest` - a real outbound HTTP request
* causes `doClose` to hang for about a minute, suspected to be an unclosed `HttpClient`'s default
* thread pool blocking native isolate teardown) - those extend [AbstractServerTest] directly
* instead of this class.
*/
abstract class AbstractHttpServerTest : AbstractServerTest() {
@Nested
@WireMockTest
inner class HttpTests {
@Test
fun `http headers`(wwRuntimeInfo: WireMockRuntimeInfo) {
stubFor(get(urlEqualTo("/foo.pkl")).willReturn(ok("foo = 1")))
val evaluatorId =
client.sendCreateEvaluatorRequest(
http =
Http(
caCertificates = null,
proxy = null,
rewrites = null,
headers = mapOf("**" to mapOf("X-Foo" to listOf("Foo"))),
)
)
client.send(
EvaluateRequest(
1,
evaluatorId,
URI("repl:text"),
"res = import(\"${wwRuntimeInfo.httpBaseUrl}/foo.pkl\")",
"output.text",
)
)
val response = client.receive<EvaluateResponse>()
assertThat(response.error).isNull()
verify(getRequestedFor(urlEqualTo("/foo.pkl")).withHeader("X-Foo", equalTo("Foo")))
}
}
}
@@ -13,24 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.server
package org.pkl.commons.test.server
import com.github.tomakehurst.wiremock.client.WireMock.equalTo
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.ok
import com.github.tomakehurst.wiremock.client.WireMock.stubFor
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import com.github.tomakehurst.wiremock.client.WireMock.verify
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.net.URI
import java.nio.file.Path
import java.util.concurrent.AbstractExecutorService
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import kotlin.io.path.createDirectories
import kotlin.io.path.outputStream
@@ -39,21 +32,48 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.msgpack.core.MessagePack
import org.pkl.commons.test.PackageServer
import org.pkl.commons.test.debugRendering
import org.pkl.core.messaging.Messages.*
import org.pkl.core.module.PathElement
import org.pkl.server.*
@Suppress("FunctionName")
abstract class AbstractServerTest {
companion object {
/** Set to `true` to bypass messagepack serialization when running [JvmServerTest]. */
internal const val USE_DIRECT_TRANSPORT = false
/** Set to `true` to bypass messagepack serialization when running JvmServerTest. */
const val USE_DIRECT_TRANSPORT = false
lateinit var executor: ExecutorService
fun createDirectExecutor(): ExecutorService =
object : AbstractExecutorService() {
override fun execute(command: Runnable) {
command.run()
}
override fun shutdown() {}
override fun shutdownNow(): MutableList<Runnable> {
throw UnsupportedOperationException("shutdownNow")
}
override fun isShutdown(): Boolean {
throw UnsupportedOperationException("isShutdown")
}
override fun isTerminated(): Boolean {
throw UnsupportedOperationException("isTerminated")
}
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
throw UnsupportedOperationException("awaitTermination")
}
}
@BeforeAll
@JvmStatic
fun beforeAll() {
@@ -643,7 +663,8 @@ abstract class AbstractServerTest {
fun `read and evaluate module path from jar`(@TempDir tempDir: Path) {
val jarFile = tempDir.resolve("resource1.jar")
jarFile.outputStream().use { outStream ->
javaClass.getResourceAsStream("resource1.jar")!!.use { inStream ->
javaClass.getResourceAsStream("/org/pkl/commons/test/server/resource1.jar")!!.use { inStream
->
inStream.copyTo(outStream)
}
}
@@ -933,7 +954,7 @@ abstract class AbstractServerTest {
writerA.start()
writerB.start()
done.await(30, java.util.concurrent.TimeUnit.SECONDS)
done.await(30, TimeUnit.SECONDS)
pipeOut.close()
reader.join(10_000)
@@ -1112,38 +1133,7 @@ abstract class AbstractServerTest {
.contains("Rewrite rule must end with '/', but was 'https://example.com'")
}
@Nested
@WireMockTest
inner class HttpTests {
@Test
fun `http headers`(wwRuntimeInfo: WireMockRuntimeInfo) {
stubFor(get(urlEqualTo("/foo.pkl")).willReturn(ok("foo = 1")))
val evaluatorId =
client.sendCreateEvaluatorRequest(
http =
Http(
caCertificates = null,
proxy = null,
rewrites = null,
headers = mapOf("**" to mapOf("X-Foo" to listOf("Foo"))),
)
)
client.send(
EvaluateRequest(
1,
evaluatorId,
URI("repl:text"),
"res = import(\"${wwRuntimeInfo.httpBaseUrl}/foo.pkl\")",
"output.text",
)
)
val response = client.receive<EvaluateResponse>()
assertThat(response.error).isNull()
verify(getRequestedFor(urlEqualTo("/foo.pkl")).withHeader("X-Foo", equalTo("Foo")))
}
}
private fun TestTransport.sendCreateEvaluatorRequest(
protected fun TestTransport.sendCreateEvaluatorRequest(
requestId: Long = 123,
resourceReaders: List<ResourceReaderSpec> = listOf(),
moduleReaders: List<ModuleReaderSpec> = listOf(),
Loaded 100 of 742 files, more files were not shown because too many files have changed in this diff. Show more