Skip to content

feat(json-c): add json-c/json-c formula - #121

Merged
MeteorsLiu merged 3 commits into
mainfrom
fennoai/issue-39-1785407810
Aug 4, 2026
Merged

feat(json-c): add json-c/json-c formula#121
MeteorsLiu merged 3 commits into
mainfrom
fennoai/issue-39-1785407810

Conversation

@fennoai

@fennoai fennoai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Closes #39.

Translates the Conan Center json-c recipe (snapshot ffe30df101afd4dc95aac2f14b25bf345e64d7be, version 0.18) into an idiomatic LLAR Formula.

Module

  • json-c/json-c/versions.json — path json-c/json-c, no dependency fallbacks (json-c has none).
  • json-c/json-c/json-c-0.18-20240915/Jsonc_llar.goxfromVer "json-c-0.18-20240915", the exact upstream tag Conan records for 0.18.

Translation notes

  • No dependencies. The Conan recipe declares no requirements/tool_requires/test_requires, and the upstream 0.18 source confirms json-c is self-contained, so there is no onRequire and deps is empty.
  • Build (CMake). Uses the LLAR CMake helper with the library-only configuration the Conan recipe applies for >= 0.17: BUILD_STATIC_LIBS=ON/BUILD_SHARED_LIBS=OFF (Conan default shared=False), DISABLE_STATIC_FPIC=OFF (Conan default fPIC=True), BUILD_TESTING=OFF, BUILD_APPS=OFF. CMAKE_INSTALL_LIBDIR=lib normalizes the layout so the installed json-c.pc and the dependency search paths resolve without lib64 handling.
  • Metadata. Derived from the installed json-c.pc via pkg-config --cflags --libs --static json-c, so the consumer flags include the private -lm json-c uses on UNIX — matching the Conan package_info (system_libs = ["m"] on Linux/FreeBSD for >= 0.17).
  • Test. onTest mirrors the Conan test_package: it writes a small consumer that includes <json-c/json.h>, links the installed json-c::json-c CMake target, builds it in a tree separate from the onBuild scratch tree, and runs it. This keeps the test valid on a cache hit (where onBuild is skipped).
  • fromVer boundary. Only the exact 0.18 tag is claimed as verified per the issue's instruction; no unverified version range is asserted.

Validation

Built and installed with the local goplus/llar CLI (installed exactly as CI does). All passing (exit 0):

  • llar test -v ./json-c/json-c@json-c-0.18-20240915 — fresh build + consumer run.
  • Re-run of the same selection — cache-hit onTest (onBuild skipped) still passes.
  • llar test ./json-c/json-c@json-c-0.18-20240915 --os linux --arch amd64 — explicit host matrix.
  • llar test --verbose ./json-c/json-c --os linux --arch amd64 — the exact CI command; resolves to the latest tag (json-c-0.19-20260627), which fromVer also serves, and builds/tests cleanly.

The installed tree contains lib/libjson-c.a, include/json-c/*.h, lib/pkgconfig/json-c.pc, and lib/cmake/json-c/, and the consumer prints { "Categories": [ "c", "c++" ] }.

Note on options. The Conan recipe exposes shared/fPIC options. I did not model them as Formula target.options because the currently-released goplus/llar build phase does not apply requested --option values to the module instance that runs onBuild/onTest (internal/modules/convertToModules builds the formula without injectMatrix), so a non-default --option selection is silently ignored and always falls back to the Formula defaults. Exposing options that cannot be honored would be misleading, so this Formula builds the Conan default (static + fPIC) directly. Once llar threads options into the build phase, shared/fPIC can be added as target.options in a follow-up.

Translate the Conan Center json-c recipe (snapshot
ffe30df101afd4dc95aac2f14b25bf345e64d7be, version 0.18) into an LLAR
Formula serving upstream tag json-c-0.18-20240915.

The build uses the CMake helper with the library-only configuration the
Conan recipe selects for its defaults: static archive with -fPIC,
BUILD_TESTING and BUILD_APPS off, and a normalized lib/ install layout.
Consumer metadata is derived from the installed json-c.pc pkg-config
file (static query, so the private -lm dependency is reported). onTest
compiles and runs the Conan test_package consumer against the installed
CMake package in a build tree independent of the onBuild scratch tree so
it also passes on a cache hit.

No dependencies: the upstream source and the Conan recipe declare none.

Co-authored-by: fennoai[bot] <231223108+fennoai[bot]@users.noreply.github.com>
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FennoAI Review — json-c/json-c Formula

Overall this is a well-structured Formula that follows the LLAR conventions closely: id, versions.json.path, and the directory json-c/json-c all agree; the filename stem Jsonc is a valid Go identifier; fromVer is a non-empty string literal aligned with the directory; c.configure/c.build/c.install are called without result assignment or ! as required; os.mkdirAll/os.writeFile use the ! failure form; and the capout + lastErr + ctx.setMetadata metadata block matches the reference. The cmake defines and the -lm/pkg-config comments were verified accurate against the json-c-0.18-20240915 source (json-c.pc.in Libs.private: @LIBS@, upstream DISABLE_STATIC_FPIC branch). Performance and security passes found no issues.

Two low-severity findings on the onTest consumer block are left inline. Neither is a correctness bug; both are about the temp-checkout convention and comment accuracy.

Reviewers should still confirm BUILD_STATIC_LIBS, BUILD_APPS, and DISABLE_STATIC_FPIC exist in the CMake of this exact upstream tag, and that llar test passes after both a fresh build and a cache hit.

onTest ctx => {
installDir := ctx.outputDir

// Build the consumer in a tree independent of the onBuild scratch tree so

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onTest synthesizes consumer.c and CMakeLists.txt and writes them into ctx.SourceDir (testDir := ctx.SourceDir + "/_llar_consumer"), mutating the temporary upstream checkout. The reference's "Installed-Output Tests" section says test source files should come from the fresh ctx.SourceDir checkout or from files shipped in the Formula module (read via ctx.Proj.readFile); the example treats ctx.SourceDir + "/consumer" as a pre-existing subdir, not a place to generate files. Consider shipping consumer.c/CMakeLists.txt in the Formula module (read with ctx.Proj.readFile) or writing them to a scratch dir outside the source checkout.

Also, the comment's stated reason is inaccurate: the separate tree is not what makes the test "run on a cache hit." On a cache hit LLAR creates a fresh ctx.SourceDir and runs onTest against the persisted ctx.outputDir, so onBuild's _build never exists there regardless. A build tree separate from _build is the right convention, but its real purpose is avoiding collision with onBuild's tree during a cache-miss build-then-test run — not enabling cache-hit tests. Suggest rewording to attribute it to that (or to convention).


id "json-c/json-c"

fromVer "json-c-0.18-20240915"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fennoai check this comparator, does this formula need a custom comparator?

@fennoai fennoai Bot Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparator check — no custom comparator needed

Short answer: no. json-c/json-c is fine on LLAR's default GNU-style numeric-segment comparator. You do not need a Jsonc_cmp.gox.

Why. Per the formula reference, without a _cmp.gox LLAR compares version strings with its GNU-style comparator (numeric runs compared numerically, non-numeric runs lexically), and you should "add one comparator only when actual upstream tags require a different ordering." json-c's upstream tags all share one shape:

json-c-0.15-20200726
json-c-0.16-20220414
json-c-0.17-20230812
json-c-0.18-20240915   <- this Formula's fromVer
json-c-0.19-20260627   <- current latest

Format is json-c-<MAJOR>.<MINOR>-<YYYYMMDD>. Under GNU comparison:

  • The shared json-c- prefix and the 0. segment tie.
  • The minor segment decides: 18 < 19 numerically → correct ordering.
  • The -YYYYMMDD suffix only ever acts as a monotonically-increasing tie-breaker (dates rise with releases), so it never contradicts the version ordering.

So the ordering the engine produces already matches upstream release order, and the single fromVer "json-c-0.18-20240915" threshold selects exactly the intended range (0.18 and newer). Your own validation confirms this end-to-end: llar test --verbose ./json-c/json-c --os linux --arch amd64 resolved to the latest tag json-c-0.19-20260627 and this Formula served it, which only works if 0.18-... is correctly ordered not newer than 0.19-....

When you would need one. A custom _cmp.gox (compareVer(a, b) => semver.Compare(...) or a hand-rolled rule) is only warranted if upstream tags mixed orderings the GNU comparator gets wrong — e.g. non-zero-padded segments that sort lexically wrong, pre-release/rc suffixes, or a numbering scheme change. json-c has none of those, and semver.Compare would actually be wrong here since these tags are not valid Go semver strings. The reference also warns to keep exactly one comparator per module, so adding an unnecessary one is a net negative.

Recommendation: leave it as-is.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fennoai test it with newer version, i need a verification report.

@fennoai fennoai Bot Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification report — newer version (json-c-0.19-20260627) ✅

Ran the real goplus/llar toolchain against this Formula, installed exactly as CI does, targeting the newer upstream tag. The fromVer "json-c-0.18-20240915" threshold correctly served the newer 0.19 source, and every run passed (exit 0).

Environment

Component Version
llar built from goplus/llar@maingo install -ldflags="-checklinkname=0" ./cmd/llar (verbatim CI step)
go 1.24.0 (auto-selected from .llar-source/go.mod)
cmake 3.22.1
gcc 11.4.0
pkg-config 0.29.2
host linux / amd64

Commands & results

# Command Resolved source Result
1 llar test --verbose ./json-c/json-c --os linux --arch amd64 (exact CI command) json-c-0.19-20260627 ✅ exit 0 — fresh build + install + consumer run
2 Re-run of #1 (cache-hit path) json-c-0.19-20260627 ✅ exit 0 — onBuild skipped, onTest rebuilt & ran the consumer
3 llar test -v ./json-c/json-c@json-c-0.19-20260627 --os linux --arch amd64 (explicit newer selector) json-c-0.19-20260627 ✅ exit 0

What was verified

  • Comparator/selection works with the newer tag. With no version pinned (run feat: add LLAR formula SKILL.md #1), the selector resolved the latest upstream tag json-c-0.19-20260627 and served it from the 0.18 Formula — confirming the default GNU comparator orders 0.18-… < 0.19-… correctly, end-to-end. Explicitly pinning @json-c-0.19-20260627 (run build(zlib): migrate from cmake to autotools build system #3) selects the same Formula and passes.
  • Build. Static archive built and linked cleanly: [100%] Linking C static library libjson-c.aBuilt target json-c, installed under debug configuration.
  • Installed tree (…/json-c@json-c-0.19-20260627-amd64-linux):
    • lib/libjson-c.a
    • include/json-c/*.h (18 headers)
    • lib/cmake/json-c/ (json-c-config.cmake, json-c-targets.cmake, json-c-targets-debug.cmake)
    • lib/pkgconfig/json-c.pcVersion: 0.19
  • Metadata. pkg-config --static correctly surfaces the private math dep:
    -I…/include -I…/include/json-c -L…/lib -lm -ljson-c
    
  • Consumer / onTest. The test_package-style consumer (find_package(json-c REQUIRED CONFIG) + json-c::json-c) built and ran, printing the expected:
    { "Categories": [ "c", "c++" ] }
    
  • Cache-hit correctness. Run feat: add zlib formula (madler/zlib) #2 exercised the cache-hit path — onBuild was skipped, yet onTest independently reconfigured, rebuilt, and ran the consumer in its own tree (_llar_consumer), still printing the expected JSON. This validates the "test valid on a cache hit" design note.

Notes

  • Only the linux/amd64 host was exercised here (the sandbox platform). CI additionally covers linux-arm64, darwin-amd64, and darwin-arm64; those were not run in this environment.
  • Confirms the earlier comparator finding: no custom _cmp.gox is needed — the newer version resolves and builds correctly under the default comparator.

Conclusion: the Formula builds, installs, and passes its consumer test against the newer json-c-0.19-20260627 source, on both fresh-build and cache-hit paths. No changes required.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fennoai i see conan support version from 0.14 to 0.19, why don't we support it? https://github.com/conan-io/conan-center-index/raw/refs/heads/master/recipes/json-c/all/conanfile.py

@fennoai fennoai Bot Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this Formula starts at 0.18 (and what wider support would take)

Good question. Two separate reasons — one is a deliberate scoping choice, the other is real technical work the current single Formula does not yet do. Also a small correction on the range.

1. Conan's tracked range is 0.140.18, not 0.19

The recipe you linked is the source tree, but Conan Center only packages the versions in config.yml:

versions:
  "0.18": {folder: all}
  "0.17": {folder: all}
  "0.16": {folder: all}
  "0.15": {folder: all}
  "0.14": {folder: all}

So Conan itself tops out at 0.18. 0.19 exists upstream (json-c released json-c-0.19-20260627) and this Formula already serves it via the 0.18 fromVer — verified in the previous run — but Conan has no 0.19 recipe entry yet.

2. fromVer is a lower bound, so older requests currently get no Formula

Per the formula reference: "For a requested version, LLAR … selects the greatest remaining fromVer not newer than the request. A request older than every fromVer has no Formula."

With only fromVer "json-c-0.18-20240915", anything below 0.18 resolves to nothing. Demonstrated live just now:

$ llar test -v ./json-c/json-c@json-c-0.16-20220414 --os linux --arch amd64
Error: failed to load modules: no formula found for json-c/json-c

So today the Formula intentionally claims only 0.18+ — matching the PR's stated boundary: "Only the exact 0.18 tag is claimed as verified … no unverified version range is asserted."

3. Older versions need different build logic — you can't just lower fromVer

This is the real reason it isn't a one-line change. The Conan recipe branches its build on the version in three places, so 0.140.16 do not build the same way 0.18 does:

# generate()
if Version(self.version) >= "0.15":
    tc.variables["BUILD_STATIC_LIBS"]  = not self.options.shared
    tc.variables["DISABLE_STATIC_FPIC"] = not self.options.get_safe("fPIC", True)
tc.cache_variables["BUILD_TESTING"] = False
if Version(self.version) < "0.17":
    # relocatable shared libs on macOS + CMake 4 support
    tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0042"]   = "NEW"
    tc.cache_variables["CMAKE_POLICY_VERSION_MINIMUM"]   = "3.5"
else:
    tc.cache_variables["BUILD_APPS"] = False

# package_info()
if Version(self.version) >= "0.17" and self.settings.os in ["Linux", "FreeBSD", "Neutrino"]:
    self.cpp_info.system_libs.extend(["m"])

Translated to what each range needs:

Requested version BUILD_STATIC_LIBS/DISABLE_STATIC_FPIC BUILD_APPS=OFF CMP0042 + CMAKE_POLICY_VERSION_MINIMUM=3.5 system_libs
0.14 ❌ not set (no static/shared split yet) ✅ needed none
0.150.16 ✅ needed none
0.170.19 (this Formula) -lm on Linux/FreeBSD

Our current onBuild hardcodes the >= 0.17 column: it sets BUILD_APPS=OFF, omits the older CMake-policy shims, and its metadata derives -lm from the installed .pc. If we just lowered fromVer to 0.14:

  • 0.14 would likely fail configure (it predates the BUILD_STATIC_LIBS split, and without CMAKE_POLICY_VERSION_MINIMUM=3.5 it can break under CMake ≥ 4).
  • 0.15/0.16 would miss the CMP0042/policy-minimum shims and could fail on newer CMake / macOS.
  • -lm would still appear via pkg-config --static on all of them, which is harmless, but the build differences above are not.

How to actually support the older range (if you want it)

fromVer describes a range with a single build recipe, so a version-conditional recipe needs multiple Formulas, one per boundary the build logic changes at:

  • json-c/json-c/json-c-0.17-.../fromVer "json-c-0.17-..." (essentially today's recipe; serves 0.170.19)
  • json-c/json-c/json-c-0.15-.../fromVer "json-c-0.15-..." with the < 0.17 policy shims and no BUILD_APPS/-lm
  • json-c/json-c/json-c-0.14-.../fromVer "json-c-0.14-..." without the static/shared split

LLAR then picks the greatest fromVer not newer than the request, so each older tag lands on the Formula whose flags match. Each new Formula must be built + llar test-verified against its own tags before its range is claimed — which is exactly why this PR scoped to the verified 0.18+ recipe rather than asserting an unverified 0.14+ range.

Recommendation

Keep this PR as the 0.17+ recipe (it's verified and correct). If broader coverage is wanted, add the 0.15 and 0.14 Formulas as a follow-up, each with its version-appropriate flags and its own verification run. I can draft those if you'd like — just say the word and I'll open the work.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fennoai support 0.17, please

@fennoai fennoai Bot Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.17 support added ✅ — pushed c25bd0e

Lowered the Formula's fromVer boundary to json-c-0.17-20230812 and verified the widened range end-to-end. Because 0.170.19 share the same Conan build branch (the >= 0.17 column: BUILD_STATIC_LIBS split, BUILD_APPS=OFF, and system_libs=["m"] on Linux/FreeBSD), no onBuild change was needed — only the threshold and the aligned directory name.

Change

- json-c/json-c/json-c-0.18-20240915/Jsonc_llar.gox   (renamed)
+ json-c/json-c/json-c-0.17-20230812/Jsonc_llar.gox

- fromVer "json-c-0.18-20240915"
+ fromVer "json-c-0.17-20230812"

One file changed, 1 insertion / 1 deletion (plus the directory rename). versions.json is unchanged (deps is empty, so there's no per-version key to add).

Verification (goplus/llar@main, go install, linux/amd64)

Requested Resolves to this Formula? Result
json-c-0.17-20230812 (new lower bound) ✅ exit 0 — fresh build, pkg-config Version: 0.17, consumer printed { "Categories": [ "c", "c++" ] }, flags … -lm -ljson-c
json-c-0.18-20240915 ✅ exit 0 — still builds
unpinned → json-c-0.19-20260627 (exact CI command) ✅ exit 0 — still builds
json-c-0.16-20220414 ❌ (below 0.17) no formula found — correct; 0.16 needs the different < 0.17 build branch and is intentionally still out of range

So the Formula now serves 0.170.19 from one recipe, and the 0.16/0.140.15 gap remains correctly unclaimed (those need their own version-conditional Formulas, per the earlier analysis).

Notes

  • Verified on linux/amd64 only (sandbox platform). CI additionally covers linux-arm64, darwin-amd64, darwin-arm64, which were not run here.
  • Commit authored as you (MeteorsLiu), committed by FennoAI, pushed to fennoai/issue-39-1785407810 (1a5354a..c25bd0e).

Extend the json-c/json-c Formula's served range down to json-c-0.17.
0.17 shares the same Conan build branch as 0.18/0.19 (BUILD_STATIC_LIBS
split, BUILD_APPS=OFF, and system_libs=[m] on Linux/FreeBSD, all gated on
>= 0.17), so no onBuild change is required — only the fromVer threshold
and the aligned directory name.

Verified with the local goplus/llar CLI (llar test --verbose, linux/amd64):
- json-c-0.17-20230812: fresh build + consumer run, pkg-config Version 0.17.
- json-c-0.18-20240915: still resolves to this Formula and builds.
- unpinned (latest json-c-0.19-20260627): still resolves and builds.
- json-c-0.16-20220414: correctly still has no Formula (below 0.17).
@MeteorsLiu
MeteorsLiu merged commit 699f3a3 into main Aug 4, 2026
10 checks passed
@fennoai
fennoai Bot deleted the fennoai/issue-39-1785407810 branch August 4, 2026 05:24
@fennoai fennoai Bot mentioned this pull request Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Translate Conan Center json-c recipe to LLAR

1 participant