diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-30 16:24:31 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-30 16:41:10 +0900 |
| commit | dfd9e72b1786597db643d50c7a55d1fcbadf7802 (patch) | |
| tree | 0d01d8547e4ddc372931b3a07d2467a0a5847c61 | |
| parent | 2f6689c3c1476c40a7c722491c1fcd5763e7d55f (diff) | |
| download | php-shirabe-dfd9e72b1786597db643d50c7a55d1fcbadf7802.tar.gz php-shirabe-dfd9e72b1786597db643d50c7a55d1fcbadf7802.tar.zst php-shirabe-dfd9e72b1786597db643d50c7a55d1fcbadf7802.zip | |
build(nix): add a package output producing the binary
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | crates/shirabe/build.rs | 38 | ||||
| -rw-r--r-- | flake.lock | 18 | ||||
| -rw-r--r-- | flake.nix | 102 | ||||
| -rwxr-xr-x | scripts/linters/lint | 2 | ||||
| -rw-r--r-- | scripts/linters/src/Linters/ComposerPin.php | 63 |
7 files changed, 226 insertions, 5 deletions
@@ -1,4 +1,5 @@ /target +/result /crates/shirabe/tests/plugin/fixtures/e2e/ext/ /crates/shirabe/tests/plugin/fixtures/e2e-normalize/ext/ /crates/shirabe/tests/plugin/fixtures/e2e-installers/ext/ @@ -27,7 +27,6 @@ Building Shirabe requires: * Direnv (optional) Nix flake supported; use `nix develop` to enter the development shell. -TODO: support `nix build` ## Build @@ -38,6 +37,12 @@ $ composer install --no-dev --working-dir=composer $ cargo build --release ``` +### Nix + +``` +$ nix build +``` + ## Test diff --git a/crates/shirabe/build.rs b/crates/shirabe/build.rs index e11b1a06..59d11ab8 100644 --- a/crates/shirabe/build.rs +++ b/crates/shirabe/build.rs @@ -16,9 +16,30 @@ fn git(repo_root: &std::path::Path, args: &[&str]) -> Option<String> { Some(String::from_utf8(output.stdout).ok()?.trim().to_string()) } -fn main() { - println!("cargo::rerun-if-changed=build.rs"); +fn from_env() -> Option<(String, Option<i64>)> { + let release_date = std::env::var("SHIRABE_RELEASE_DATE").ok(); + let dev_warning_time = std::env::var("SHIRABE_DEV_WARNING_TIME").ok(); + match (release_date, dev_warning_time) { + (Some(release_date), Some(dev_warning_time)) => { + let dev_warning_time = if dev_warning_time.is_empty() { + None + } else { + Some(dev_warning_time.parse().expect( + "SHIRABE_DEV_WARNING_TIME holds a Unix timestamp, or nothing at all for a \ + tagged release", + )) + }; + Some((release_date, dev_warning_time)) + } + (None, None) => None, + _ => panic!( + "SHIRABE_RELEASE_DATE and SHIRABE_DEV_WARNING_TIME both describe the commit the build \ + comes from: set them together, or set neither and build from a git checkout" + ), + } +} +fn from_git() -> (String, Option<i64>) { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let repo_root = std::path::Path::new(&manifest_dir) .parent() @@ -67,6 +88,19 @@ fn main() { Some(commit_time + 60 * 86400) }; + (release_date, dev_warning_time) +} + +fn main() { + println!("cargo::rerun-if-changed=build.rs"); + println!("cargo::rerun-if-env-changed=SHIRABE_RELEASE_DATE"); + println!("cargo::rerun-if-env-changed=SHIRABE_DEV_WARNING_TIME"); + + let (release_date, dev_warning_time) = match from_env() { + Some(values) => values, + None => from_git(), + }; + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); std::fs::write(out_dir.join("release_date.rs"), format!("{release_date:?}")).unwrap(); std::fs::write( @@ -1,5 +1,22 @@ { "nodes": { + "composer": { + "flake": false, + "locked": { + "lastModified": 1776166312, + "narHash": "sha256-cmz5YaxfykkUlF7Ai0Yu8L6sfNePmx3v24g6131+/RM=", + "owner": "composer", + "repo": "composer", + "rev": "82a2fbd1372a98d7915cfb092acf05207d9b4113", + "type": "github" + }, + "original": { + "owner": "composer", + "ref": "2.9.7", + "repo": "composer", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": [ @@ -38,6 +55,7 @@ }, "root": { "inputs": { + "composer": "composer", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay", @@ -6,6 +6,11 @@ systems.url = "github:nix-systems/default"; + composer = { + url = "github:composer/composer/2.9.7"; + flake = false; + }; + flake-utils = { url = "github:numtide/flake-utils"; inputs.systems.follows = "systems"; @@ -24,7 +29,9 @@ outputs = { + self, nixpkgs, + composer, flake-utils, rust-overlay, treefmt-nix, @@ -42,15 +49,106 @@ imports = [ ./treefmt.nix ]; programs.rustfmt.package = rustToolchain; }; + + php = pkgs.php85; + + composerVersion = + let + versionLines = builtins.filter (match: match != null) ( + map (line: builtins.match " *public const VERSION = '(.*)';" line) ( + pkgs.lib.splitString "\n" (builtins.readFile "${composer}/src/Composer/Composer.php") + ) + ); + in + builtins.head (builtins.head versionLines); + + composerRuntime = pkgs.stdenvNoCC.mkDerivation { + pname = "composer-runtime"; + version = composerVersion; + src = composer; + nativeBuildInputs = [ + php + php.packages.composer + ]; + dontPatchShebangs = true; + buildPhase = '' + runHook preBuild + export COMPOSER_HOME="$TMPDIR/composer-home" + export COMPOSER_CACHE_DIR="$TMPDIR/composer-cache" + export COMPOSER_ROOT_VERSION="${composerVersion}" + composer install --no-dev --no-interaction --no-progress + runHook postBuild + ''; + installPhase = '' + runHook preInstall + cp -r . "$out" + runHook postInstall + ''; + + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + outputHash = "sha256-owdTo9YBaQv3a3FnUubBfI0gjca5XEJ84/PukcukPfc="; + }; + + commitTime = self.lastModified; + commitDate = self.lastModifiedDate; + + rustPlatform = pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; + + shirabe = rustPlatform.buildRustPackage { + pname = "shirabe"; + version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.version; + + src = pkgs.lib.fileset.toSource { + root = ./.; + fileset = pkgs.lib.fileset.unions [ + ./Cargo.toml + ./Cargo.lock + ./crates + ]; + }; + cargoLock.lockFile = ./Cargo.lock; + + postPatch = '' + cp -r ${composerRuntime} composer + chmod -R u+w composer + ''; + + env = { + SHIRABE_RELEASE_DATE = + let + field = start: builtins.substring start 2 commitDate; + in + "${builtins.substring 0 4 commitDate}-${field 4}-${field 6} ${field 8}:${field 10}:${field 12}"; + SHIRABE_DEV_WARNING_TIME = toString (commitTime + 60 * 86400); + }; + + doCheck = false; + + meta = { + description = "Rust port of Composer, the dependency manager for PHP"; + homepage = "https://github.com/nsfisis/php-shirabe"; + license = pkgs.lib.licenses.mit; + mainProgram = "shirabe"; + }; + }; in { formatter = treefmt.config.build.wrapper; + packages = { + inherit shirabe; + default = shirabe; + }; + devShells.default = pkgs.mkShell { packages = [ rustToolchain - pkgs.php85 - pkgs.php85Packages.composer + php + php.packages.composer pkgs.just # The following softwares are used for testing: diff --git a/scripts/linters/lint b/scripts/linters/lint index 150e1f1a..7e7c5fe6 100755 --- a/scripts/linters/lint +++ b/scripts/linters/lint @@ -6,6 +6,7 @@ declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; use Shirabe\Lint\Linters\CargoWorkspaceDependencies; +use Shirabe\Lint\Linters\ComposerPin; use Shirabe\Lint\Linters\ContiguousUseBlock; use Shirabe\Lint\Linters\NoBannedUse; use Shirabe\Lint\Linters\NoDecorativeSectionComment; @@ -24,6 +25,7 @@ $rootDir = dirname(__DIR__, 2); $runner = new Runner($rootDir, [ [new CargoWorkspaceDependencies(), []], + [new ComposerPin(), []], [new ContiguousUseBlock(), []], [new NoBannedUse(), []], [new NoDecorativeSectionComment(), [ diff --git a/scripts/linters/src/Linters/ComposerPin.php b/scripts/linters/src/Linters/ComposerPin.php new file mode 100644 index 00000000..d1db55fe --- /dev/null +++ b/scripts/linters/src/Linters/ComposerPin.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +namespace Shirabe\Lint\Linters; + +use Shirabe\Lint\Linter; + +/** + * The Composer sources are pinned twice: as the `composer` submodule, which a `cargo` build reads, + * and as the `composer` flake input, which a `nix build` reads in its place because a flake source + * carries no submodule. Both have to name the same commit, or the two builds embed different + * Composer runtimes. + */ +final class ComposerPin implements Linter +{ + public function name(): string + { + return 'composer_pin'; + } + + public function failureIntro(): string + { + return "The `composer` submodule and the `composer` flake input name different commits.\n" + . 'Point the input at the submodule commit and run `nix flake lock`:'; + } + + public function check(string $rootDir, array $excludes): array + { + $submoduleRev = $this->submoduleRev($rootDir); + $inputRev = $this->inputRev($rootDir); + + if ($submoduleRev === $inputRev) { + return []; + } + + return [ + "submodule: {$submoduleRev}", + "flake input: {$inputRev}", + ]; + } + + private function submoduleRev(string $rootDir): string + { + $command = sprintf('git -C %s ls-tree HEAD composer', escapeshellarg($rootDir)); + exec($command, $output, $status); + if ($status !== 0 || $output === []) { + return '(unreadable: git ls-tree HEAD composer failed)'; + } + + // `<mode> commit <rev>\tcomposer` + $fields = preg_split('/\s+/', $output[0]); + + return $fields[2] ?? '(unreadable: unexpected git ls-tree output)'; + } + + private function inputRev(string $rootDir): string + { + $lock = json_decode((string) file_get_contents("{$rootDir}/flake.lock"), true); + + return $lock['nodes']['composer']['locked']['rev'] ?? '(missing from flake.lock)'; + } +} |
