saxonc-libs 12.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5fdef4a62ea21f77af5320cf03e7111e0825fd4ae6f48dfa2d03d8031ae943b2
4
+ data.tar.gz: de665f07f3d5e79e727259d70dcec972ca68674da06013b4d0ea2abcf5546e34
5
+ SHA512:
6
+ metadata.gz: ce93dc5784f49652fc5ef67f8644a60f88f8b0a3fc0746901183998e53a878f7d65316213f0908bec62201dca369eb9346c3d99092339ff00ba18d58cbdab077
7
+ data.tar.gz: 73ba3950646c92563d8a9ec063808f4d84bf92269615363d3c0166c4bb152c14165839a44a145c183c88f489bfae06f64757acefd58ef62d0b3791a451dcccff
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2025 SaxonC Ruby Contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # SaxonC Libs
2
+
3
+ SaxonC Libs packages the pre-built SaxonC native libraries for use
4
+ from Ruby gems such as [`saxonc`](../saxon-rb). It offers a single
5
+ command that downloads the official SaxonC distribution for the
6
+ current platform, extracts the `lib`, `include`, and `bin` artifacts,
7
+ and stores them in a predictable location other gems can depend on.
8
+
9
+ ## Versioning
10
+
11
+ This gem mirrors SaxonC releases using the pattern
12
+ `<saxon-version>.<packaging-patch>`. For example, version `12.9.0`
13
+ wraps SaxonC `12.9` with a first packaging revision. Whenever SaxonC
14
+ publishes a new point release, bump the first two segments and reset
15
+ the packaging patch to zero.
16
+
17
+ ## Usage
18
+
19
+ ```sh
20
+ gem install saxonc-libs
21
+ saxonc-libs install --target vendor/saxonc --edition he
22
+ ```
23
+
24
+ The install command will:
25
+
26
+ 1. Detect the current platform (macOS ARM64, macOS Intel, Linux
27
+ x86_64, or Windows x86_64 for the initial release).
28
+ 2. Read the release manifest for the bundled SaxonC version and
29
+ edition (`he`, `pe`, or `ee`).
30
+ 3. Download the official SaxonC zip archive for that platform.
31
+ 4. Extract the relevant files into
32
+ `vendor/saxonc/<edition>/<platform>`.
33
+ 5. Write a manifest JSON file describing the extracted payload.
34
+
35
+ The CLI accepts `--edition` (defaults to `he`) and `--force` (to
36
+ re-download even if a cached copy exists), which is handy when testing
37
+ different SaxonC editions or refreshing a corrupted cache.
38
+
39
+ Other gems (such as `saxonc`) can then look up the installation path via:
40
+
41
+ ```ruby
42
+ require "saxonc/libs"
43
+ saxon_home = SaxonC::Libs.install(edition: :he)
44
+ # => /path/to/vendor/saxonc/he/macos-arm64
45
+ ```
46
+
47
+ ## Release manifests
48
+
49
+ The `releases.yml` file in the repo root describes where to download
50
+ each platform artifact. The installer refuses to run unless a manifest
51
+ entry exists for the detected platform and edition. Publishing a new
52
+ packaging release typically involves:
53
+
54
+ 1. Adding/updating the relevant section inside `releases.yml`.
55
+ 2. Recording the download URL and SHA256 checksum for each platform.
56
+ 3. Updating `lib/saxonc/libs/version.rb` with the new version.
57
+ 4. Releasing the gem.
58
+
59
+ You can populate missing SHA256 values automatically via
60
+ `script/fill_checksums.rb`. Run it without options to download each
61
+ archive and compute its digest, or pass `--source-dir` to point at a
62
+ directory containing previously downloaded zips (useful for PE/EE
63
+ editions that require credentials):
64
+
65
+ ```sh
66
+ bundle exec ruby script/fill_checksums.rb --source-dir ~/Downloads/saxonc
67
+ ```
68
+
69
+ ## License
70
+
71
+ This helper gem is distributed under the MIT license. The downloaded
72
+ SaxonC binaries remain subject to Saxonica's license terms; the
73
+ installer copies their notices alongside the extracted artifacts.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
data/exe/saxonc-libs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "saxonc/libs"
5
+
6
+ SaxonC::Libs::CLI.start(ARGV)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module SaxonC
6
+ module Libs
7
+ # Very small command-line interface that exposes the installer.
8
+ class CLI
9
+ def self.start(argv)
10
+ new(argv).run
11
+ end
12
+
13
+ def initialize(argv)
14
+ @argv = argv.dup
15
+ @options = { target: Installer.default_installation_dir, edition: :he, force: false }
16
+ end
17
+
18
+ def run
19
+ parser.parse!(@argv)
20
+ command = @argv.shift || "install"
21
+
22
+ case command
23
+ when "install"
24
+ Installer.new(target: @options[:target], edition: @options[:edition]).install(force: @options[:force])
25
+ else
26
+ warn "Unknown command: #{command}"
27
+ warn parser
28
+ exit 1
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def parser
35
+ @parser ||= OptionParser.new do |opts|
36
+ opts.banner = "Usage: saxonc-libs [install] [options]"
37
+ opts.on("-t", "--target=DIR", "Directory to extract the runtime into") do |dir|
38
+ @options[:target] = File.expand_path(dir)
39
+ end
40
+ opts.on("-e", "--edition=EDITION", "Saxon edition to install (he, pe, ee)") do |value|
41
+ @options[:edition] = value.downcase.to_sym
42
+ end
43
+ opts.on("-f", "--force", "Re-download even if already installed") do
44
+ @options[:force] = true
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "tempfile"
6
+ require "digest"
7
+
8
+ module SaxonC
9
+ module Libs
10
+ # Streams a remote archive to disk and verifies its checksum.
11
+ class Download
12
+ CHUNK_SIZE = 1024 * 1024
13
+
14
+ def initialize(url, checksum: nil)
15
+ @url = URI.parse(url)
16
+ @checksum = checksum
17
+ end
18
+
19
+ def fetch
20
+ tempfile = Tempfile.new(["saxonc", File.extname(@url.path)])
21
+ tempfile.binmode
22
+ digest = Digest::SHA256.new if @checksum
23
+
24
+ Net::HTTP.start(@url.host, @url.port, use_ssl: @url.scheme == "https") do |http|
25
+ request = Net::HTTP::Get.new(@url)
26
+ http.request(request) do |response|
27
+ unless response.is_a?(Net::HTTPSuccess)
28
+ raise "Failed to download #{@url} (#{response.code})"
29
+ end
30
+
31
+ response.read_body do |chunk|
32
+ tempfile.write(chunk)
33
+ digest&.update(chunk)
34
+ end
35
+ end
36
+ end
37
+
38
+ tempfile.flush
39
+ tempfile.rewind
40
+ verify_checksum(digest)
41
+ path = tempfile.path
42
+ tempfile.close
43
+ path
44
+ ensure
45
+ tempfile&.close unless tempfile.nil? || tempfile.closed?
46
+ end
47
+
48
+ private
49
+
50
+ def verify_checksum(digest)
51
+ return unless @checksum
52
+
53
+ actual = digest.hexdigest
54
+ return if actual == @checksum.downcase
55
+
56
+ raise "Checksum mismatch for #{@url}: expected #{@checksum}, got #{actual}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "time"
6
+ require "zip"
7
+
8
+ require_relative "platform"
9
+ require_relative "releases"
10
+ require_relative "download"
11
+
12
+ module SaxonC
13
+ module Libs
14
+ # Coordinates download and extraction of the SaxonC distribution release.
15
+ class Installer
16
+ attr_reader :target_dir, :platform, :release, :edition
17
+
18
+ def self.default_installation_dir
19
+ File.expand_path("../../vendor/saxonc", __dir__)
20
+ end
21
+
22
+ def initialize(target: self.class.default_installation_dir, platform: Platform.detect, edition: :he)
23
+ @target_dir = File.expand_path(target)
24
+ @platform = platform
25
+ @edition = edition.to_sym
26
+ @release = Releases.new(@edition)
27
+ end
28
+
29
+ def install(force: false)
30
+ return platform_dir if installed? && !force
31
+
32
+ FileUtils.mkdir_p(File.dirname(platform_dir))
33
+ release = release.release_for(platform.key)
34
+ url = release.fetch("url")
35
+ checksum = release["sha256"]
36
+
37
+ puts "Downloading #{url}..."
38
+ release_path = Download.new(url, checksum: checksum).fetch
39
+ puts "Extracting to #{platform_dir}..."
40
+ extract_release(release_path)
41
+ File.delete(release_path) if File.exist?(release_path)
42
+ write_release(release)
43
+ puts "SaxonC runtime ready in #{platform_dir}"
44
+ platform_dir
45
+ end
46
+
47
+ def installed?
48
+ Dir.exist?(File.join(platform_dir, "lib")) && File.exist?(File.join(platform_dir, "release.json"))
49
+ end
50
+
51
+ def platform_dir
52
+ File.join(target_dir, edition.to_s, platform.key)
53
+ end
54
+
55
+ private
56
+
57
+ def extract_release(release_path)
58
+ FileUtils.rm_rf(platform_dir)
59
+ FileUtils.mkdir_p(platform_dir)
60
+
61
+ Zip::File.open(release_path) do |zip_file|
62
+ zip_file.each do |entry|
63
+ next unless entry.file?
64
+
65
+ relative_path = case entry.name
66
+ when /^SaxonCHE\//
67
+ entry.name.sub(/^SaxonCHE\//, "")
68
+ when /^notices\//
69
+ entry.name
70
+ when /^README/i
71
+ entry.name
72
+ else
73
+ next
74
+ end
75
+
76
+ next if relative_path.empty?
77
+
78
+ destination = File.join(platform_dir, relative_path)
79
+ FileUtils.mkdir_p(File.dirname(destination))
80
+ entry.extract(destination) { true }
81
+ end
82
+ end
83
+ end
84
+
85
+ def write_release(release)
86
+ release_data = {
87
+ platform: platform.key,
88
+ saxon_version: SaxonC::Libs::SAXON_VERSION,
89
+ edition: edition.to_s,
90
+ release_url: release.fetch("url"),
91
+ extracted_at: Time.now.utc.iso8601
92
+ }
93
+
94
+ File.write(File.join(platform_dir, "release.json"), JSON.pretty_generate(release_data))
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbconfig"
4
+
5
+ module SaxonC
6
+ module Libs
7
+ # Detects and normalizes the current runtime platform.
8
+ class Platform
9
+ PLATFORM_MAP = {
10
+ "macos-arm64" => ->(os, cpu) { os =~ /darwin/ && cpu =~ /arm|aarch64/ },
11
+ "macos-x86_64" => ->(os, cpu) { os =~ /darwin/ && cpu =~ /x86_64|amd64/ },
12
+ "linux-x86_64" => ->(os, cpu) { os =~ /linux/ && cpu =~ /x86_64|amd64/ },
13
+ "windows-x86_64" => ->(os, cpu) { os =~ /mswin|mingw|cygwin/ && cpu =~ /x86_64|amd64/ }
14
+ }.freeze
15
+
16
+ attr_reader :key
17
+
18
+ def self.detect
19
+ host_os = RbConfig::CONFIG["host_os"].downcase
20
+ host_cpu = RbConfig::CONFIG["host_cpu"].downcase
21
+
22
+ key = PLATFORM_MAP.find { |_name, predicate| predicate.call(host_os, host_cpu) }&.first
23
+ raise "Unsupported SaxonC platform for #{host_os}/#{host_cpu}" unless key
24
+
25
+ new(key)
26
+ end
27
+
28
+ def initialize(key)
29
+ @key = key
30
+ end
31
+
32
+ def to_s
33
+ key
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ module SaxonC
7
+ module Libs
8
+ # Loads the releases.yml describing where to download platform release.
9
+ class Releases
10
+ attr_reader :saxon_version, :saxon_edition
11
+
12
+ def initialize(saxon_edition = :he)
13
+ @saxon_edition = saxon_edition.to_sym
14
+ end
15
+
16
+ def archive_for(platform_key)
17
+ data = releases_data
18
+ data_for_edition = data.fetch(saxon_edition) do
19
+ raise KeyError, "No release defined for Saxon edition #{saxon_edition} in #{release_path}"
20
+ end
21
+ data_for_edition.fetch(platform_key) do
22
+ raise KeyError, "No archive defined for #{platform_key} in #{release_path}"
23
+ end
24
+ end
25
+
26
+ def version
27
+ @saxon_version ||= releases_data.fetch("version")
28
+ end
29
+
30
+ private
31
+
32
+ def releases_data
33
+ @releases_data ||= begin
34
+ unless releases_path.exist?
35
+ raise IOError, "Missing releases: #{releases_path}"
36
+ end
37
+
38
+ YAML.load_file(releases_path)
39
+ end
40
+ end
41
+
42
+ def releases_path
43
+ Pathname.new(__dir__).join("..", "..", "..", "releases.yml").expand_path
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ module SaxonC
3
+ module Libs
4
+ VERSION = "12.9.1"
5
+ SAXON_VERSION = VERSION.split(".")[0..1].join(".")
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "libs/version"
4
+ require_relative "libs/installer"
5
+ require_relative "libs/cli"
6
+
7
+ module SaxonC
8
+ # Top-level namespace for the runtime helper gem.
9
+ module Libs
10
+ class << self
11
+ def install(**options)
12
+ Installer.new(**options).install
13
+ end
14
+
15
+ def ensure_installed(**options)
16
+ install(**options)
17
+ end
18
+
19
+ def find_installation(edition: :he, target: Installer.default_installation_dir, platform: nil)
20
+ resolved_platform = platform || SaxonC::Libs::Platform.detect
21
+ installer = Installer.new(target: target, edition: edition, platform: resolved_platform)
22
+ installer.platform_dir if installer.installed?
23
+ end
24
+
25
+ def default_installation_dir
26
+ Installer.default_installation_dir
27
+ end
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: saxonc-libs
3
+ version: !ruby/object:Gem::Version
4
+ version: 12.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Jakob Kofoed Janot
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ description: Downloads and installs the official SaxonC runtime files so other Ruby
56
+ gems can link against them.
57
+ email:
58
+ - jakob@janot.dk
59
+ executables:
60
+ - saxonc-libs
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - exe/saxonc-libs
68
+ - lib/saxonc/libs.rb
69
+ - lib/saxonc/libs/cli.rb
70
+ - lib/saxonc/libs/download.rb
71
+ - lib/saxonc/libs/installer.rb
72
+ - lib/saxonc/libs/platform.rb
73
+ - lib/saxonc/libs/releases.rb
74
+ - lib/saxonc/libs/version.rb
75
+ homepage: https://github.com/jakobjanot/ruby-saxonc-libs
76
+ licenses:
77
+ - MIT
78
+ metadata:
79
+ homepage_uri: https://github.com/jakobjanot/ruby-saxonc-libs
80
+ source_code_uri: https://github.com/jakobjanot/ruby-saxonc-libs
81
+ changelog_uri: https://github.com/jakobjanot/ruby-saxonc-libs/blob/main/saxonc-libs/CHANGELOG.md
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.4.19
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Platform-specific SaxonC binaries for Ruby projects
101
+ test_files: []