feat: update user configuration example and add development package support
- Enhanced the user.depot.toml.example file with additional comments and options for user-local preferences. - Bumped version from 0.35.0 to 0.35.1 in meson.build. - Introduced a new option for DEPOT_DEVELOPMENT_PACKAGE in meson_options.txt. - Added functions to handle the requested development package in build_options.rs. - Updated various builder files (autotools.rs, cmake.rs, custom.rs, meson.rs, perl.rs) to support passing build flags. - Implemented checks for the required development package during source builds in commands.rs and commands/build_cmd.rs. - Added tests to ensure the correct behavior of development package requirements and source build warnings. - Modified manual source handling in source/mod.rs to validate local manual sources before proceeding with builds. - Updated cargo build and test scripts to accept a new development package argument. - Added VSCode settings to disable automatic configuration on open.
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mesonbuild.configureOnOpen": false
|
||||
}
|
||||
Generated
+18
-18
@@ -451,7 +451,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
||||
|
||||
[[package]]
|
||||
name = "depot"
|
||||
version = "0.35.0"
|
||||
version = "0.35.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
@@ -479,7 +479,7 @@ dependencies = [
|
||||
"serde_ignored",
|
||||
"sha1 0.11.0",
|
||||
"sha2 0.11.0",
|
||||
"signal-hook 0.4.3",
|
||||
"signal-hook 0.4.4",
|
||||
"suppaftp",
|
||||
"sys-mount",
|
||||
"tar",
|
||||
@@ -1913,9 +1913,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -1972,9 +1972,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -2041,9 +2041,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e"
|
||||
checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -2337,9 +2337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
|
||||
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -2352,27 +2352,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed"
|
||||
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
@@ -3166,9 +3166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "8.4.0"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7756d0206d058333667493c4014f545f4b9603c4330ccd6d9b3f86dcab59f7d9"
|
||||
checksum = "2726508a48f38dceb22b35ecbbd2430efe34ff05c62bd3285f965d7911b33464"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bzip2",
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "depot"
|
||||
version = "0.35.0"
|
||||
version = "0.35.1"
|
||||
edition = "2024"
|
||||
|
||||
[lints.rust]
|
||||
@@ -16,18 +16,18 @@ git2 = "0.20.4"
|
||||
indicatif = "0.18.4"
|
||||
nix = { version = "0.31.2", features = ["user"] }
|
||||
rusqlite = "0.39.0"
|
||||
semver = "1.0.27"
|
||||
semver = "1.0.28"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
yaml-rust2 = "0.11.0"
|
||||
sha2 = "0.11.0"
|
||||
tar = "0.4.45"
|
||||
tempfile = "=3.27.0"
|
||||
thiserror = "2.0.18"
|
||||
toml = "1.1.0+spec-1.1.0"
|
||||
toml = "1.1.2"
|
||||
url = "2.5.8"
|
||||
walkdir = "2.5.0"
|
||||
xz2 = "0.1.7"
|
||||
zip = "8.4.0"
|
||||
zip = "8.5.0"
|
||||
zstd = { version = "0.13.3", features = ["zstdmt"] }
|
||||
inquire = "0.9.4"
|
||||
md5 = "0.8.0"
|
||||
@@ -45,7 +45,7 @@ b2sum-rust = "0.3.0"
|
||||
serde_ignored = "0.1.14"
|
||||
lz4_flex = "0.13.0"
|
||||
lzma-rust2 = "0.16.2"
|
||||
signal-hook = "0.4.3"
|
||||
signal-hook = "0.4.4"
|
||||
sha1 = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
+31
-14
@@ -1,28 +1,45 @@
|
||||
[flags]
|
||||
# Default build flags applied to all packages via /etc/depot.d/build.toml
|
||||
# Compiler selection
|
||||
cc = "gcc"
|
||||
# Example /etc/depot.d/build.toml
|
||||
#
|
||||
# Drop-in build defaults. This file is a good place for policy that should
|
||||
# apply everywhere without crowding the main /etc/depot.toml.
|
||||
|
||||
# Default CFLAGS
|
||||
# Toolchain selection.
|
||||
cc = "clang"
|
||||
# cxx = "clang++"
|
||||
# ar = "ar"
|
||||
# ld = "ld.lld"
|
||||
|
||||
# Compiler defaults. These are merged with package-level flags unless a package
|
||||
# explicitly replaces them.
|
||||
cflags = ["-O2"]
|
||||
cflags += ["-pipe"]
|
||||
# cxxflags = ["-O2", "-pipe"]
|
||||
|
||||
# Optional install directory overrides for supported builders
|
||||
#bindir = "/usr/bin"
|
||||
#libdir = "/usr/lib"
|
||||
#sysconfdir = "/etc"
|
||||
|
||||
# Default LDFLAGS
|
||||
# Linker defaults.
|
||||
ldflags = ["-Wl,-O1"]
|
||||
|
||||
# Default LTOFLAGS and automatic injection into CFLAGS/CXXFLAGS/LDFLAGS.
|
||||
# Link-time optimization policy.
|
||||
ltoflags = ["-flto=auto"]
|
||||
|
||||
# Rust-specific LTO flags appended only to RUSTFLAGS when use_lto is enabled.
|
||||
#RUSTLTOFLAGS = ["-Clinker-plugin-lto"]
|
||||
# rustltoflags = ["-Clinker-plugin-lto"]
|
||||
use_lto = true
|
||||
|
||||
# CARCH short name (can be overridden per-package)
|
||||
# Architecture defaults. Packages may still override this when needed.
|
||||
carch = "x86_64"
|
||||
# chost = "x86_64-sfg-linux-gnu"
|
||||
# cbuild = "x86_64-sfg-linux-gnu"
|
||||
|
||||
# Example of appending per-package cflags: (will be exposed as build.cflags appends)
|
||||
# Optional install directory overrides for supported builders.
|
||||
# bindir = "/usr/bin"
|
||||
# libdir = "/usr/lib"
|
||||
# sysconfdir = "/etc"
|
||||
|
||||
# Example of additive layering in depot.d:
|
||||
# cflags += ["-fstack-clash-protection"]
|
||||
# ldflags += ["-Wl,-z,relro", "-Wl,-z,now"]
|
||||
|
||||
# Avoid host-specific tuning such as -march=native in shared system configs
|
||||
# unless every package built on this machine is intended only for this machine.
|
||||
# cflags += ["-march=native"]
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
[package]
|
||||
# System-wide package overrides. Example: set default provides or package metadata
|
||||
# name = ""
|
||||
# Example /etc/depot.d/package.toml
|
||||
#
|
||||
# System-wide package metadata overrides. Use this sparingly: this is best for
|
||||
# policy defaults or local compatibility shims, not for rewriting package specs.
|
||||
|
||||
# Example: provide a virtual runtime package name to satisfy runtime deps
|
||||
# Example fields you might override globally in a controlled environment:
|
||||
# homepage = "https://mirror.example.org/packages"
|
||||
# license = ["MIT"]
|
||||
|
||||
# Example: provide a virtual package name to satisfy dependency expressions.
|
||||
# This can be useful on systems with a local compatibility package.
|
||||
# provides = ["filesystem"]
|
||||
|
||||
# Example: declare that local packages replace an older naming scheme.
|
||||
# replaces = ["old-filesystem"]
|
||||
|
||||
# Example: force a conflict relationship across locally maintained packages.
|
||||
# conflicts = ["legacy-filesystem"]
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
# Example /etc/depot.d/repos.toml
|
||||
#
|
||||
# Repository configuration for source specs and binary package feeds.
|
||||
# Lower priority numbers win first when multiple repos provide the same package.
|
||||
|
||||
[settings]
|
||||
# Prefer binary packages when both a binary package and a source spec exist.
|
||||
# Set this to false if you want source specs to be preferred by default.
|
||||
prefer_binary = true
|
||||
|
||||
[source.vertex]
|
||||
# Source repository used for spec lookup and source-build planning.
|
||||
url = "https://gitlab.com/vertex-linux/packages.git"
|
||||
enabled = true
|
||||
priority = 50
|
||||
|
||||
# Restrict scanning to selected subdirectories inside the source repo.
|
||||
subdirs = ["core", "extra"]
|
||||
|
||||
# Example:
|
||||
# branch = "main"
|
||||
|
||||
[binary.core]
|
||||
# Binary repository for core packages.
|
||||
url = "https://repo.sfgos.net/core"
|
||||
enabled = true
|
||||
priority = 10
|
||||
repo_db = "repo.db.zst"
|
||||
|
||||
[binary.core.arch.x86_64]
|
||||
# Enable this repo for x86_64 systems.
|
||||
enabled = true
|
||||
|
||||
[binary.extra]
|
||||
# Secondary binary repository for additional packages.
|
||||
url = "https://repo.sfgos.net/extra"
|
||||
enabled = true
|
||||
priority = 20
|
||||
@@ -24,3 +40,14 @@ repo_db = "repo.db.zst"
|
||||
|
||||
[binary.extra.arch.x86_64]
|
||||
enabled = true
|
||||
|
||||
# Additional examples:
|
||||
#
|
||||
# [binary.testing]
|
||||
# url = "https://repo.example.org/testing"
|
||||
# enabled = false
|
||||
# priority = 5
|
||||
# repo_db = "repo.db.zst"
|
||||
#
|
||||
# [binary.testing.arch.x86_64]
|
||||
# enabled = true
|
||||
|
||||
+67
-26
@@ -1,42 +1,83 @@
|
||||
# Example /etc/depot.toml - system-wide configuration for Depot
|
||||
# Example /etc/depot.toml
|
||||
#
|
||||
# This file is the main system-wide configuration for Depot.
|
||||
# Keep settings here for machine-wide defaults that should apply to every user.
|
||||
# For smaller drop-in overrides, prefer adding files under /etc/depot.d/.
|
||||
#
|
||||
# Most values here act as defaults and can still be overridden:
|
||||
# - per package in package specs
|
||||
# - per invocation with CLI flags
|
||||
# - per user in ~/.config/depot.toml
|
||||
|
||||
[build]
|
||||
# Installation prefix for package builds
|
||||
# Installation prefix used by supported build systems when a package does not
|
||||
# override it explicitly. For a normal system package build this is usually /usr.
|
||||
prefix = "/usr"
|
||||
# Default compiler and archiver
|
||||
cc = "gcc"
|
||||
|
||||
# Default toolchain commands. These are passed through to builders that honor
|
||||
# CC/CXX/AR and related variables.
|
||||
cc = "clang"
|
||||
ar = "ar"
|
||||
|
||||
# Optional examples:
|
||||
# cxx = "clang++"
|
||||
# ld = "ld.lld"
|
||||
# cpp = "cpp"
|
||||
# strip = "strip"
|
||||
# ranlib = "ranlib"
|
||||
|
||||
[build.flags]
|
||||
# Basic compiler flags
|
||||
# Baseline C compiler flags for packages that do not supply their own.
|
||||
# Keep these conservative unless you are intentionally tuning for one machine.
|
||||
cflags = ["-O2"]
|
||||
# Append additional flags (demonstrates += append support)
|
||||
|
||||
# Appends are often clearer than rewriting the whole list. This demonstrates
|
||||
# Depot's += support for layered config files.
|
||||
# NOTE: this is NOT regular TOML syntax. The += operator is a special extension that Depot supports
|
||||
cflags += ["-g"]
|
||||
|
||||
# Optional install directory overrides for supported builders
|
||||
#bindir = "/usr/bin"
|
||||
#sbindir = "/usr/bin"
|
||||
#libdir = "/usr/lib"
|
||||
#libexecdir = "/usr/lib"
|
||||
#sysconfdir = "/etc"
|
||||
#localstatedir = "/var"
|
||||
#sharedstatedir = "/var/lib"
|
||||
#includedir = "/usr/include"
|
||||
#datarootdir = "/usr/share"
|
||||
#datadir = "/usr/share"
|
||||
#mandir = "/usr/share/man"
|
||||
#infodir = "/usr/share/info"
|
||||
|
||||
# Linker flags
|
||||
# Optional C++ and linker defaults:
|
||||
# cxxflags = ["-O2"]
|
||||
ldflags = ["-Wl,-O1"]
|
||||
|
||||
# Target architecture short name (CARCH). Defaults to host arch if unset.
|
||||
# Link-time optimization defaults. When use_lto is true, Depot will inject
|
||||
# ltoflags into the appropriate compiler and linker flag sets automatically.
|
||||
# ltoflags = ["-flto=auto"]
|
||||
# use_lto = false
|
||||
|
||||
# Target architecture short name (CARCH). If unset, Depot derives this from the
|
||||
# current host system or active cross target.
|
||||
carch = "x86_64"
|
||||
|
||||
# Target triples (optional)
|
||||
#chost = "x86_64-pc-linux-gnu"
|
||||
#cbuild = "x86_64-pc-linux-gnu"
|
||||
# Target triples are usually only needed for cross or multilib scenarios.
|
||||
# chost = "x86_64-pc-linux-gnu"
|
||||
# cbuild = "x86_64-pc-linux-gnu"
|
||||
|
||||
# Optional install directory overrides for supported builders. Leave these at
|
||||
# their defaults unless you are intentionally targeting a non-standard layout.
|
||||
# bindir = "/usr/bin"
|
||||
# sbindir = "/usr/bin"
|
||||
# libdir = "/usr/lib"
|
||||
# libexecdir = "/usr/lib"
|
||||
# sysconfdir = "/etc"
|
||||
# localstatedir = "/var"
|
||||
# sharedstatedir = "/var/lib"
|
||||
# includedir = "/usr/include"
|
||||
# datarootdir = "/usr/share"
|
||||
# datadir = "/usr/share"
|
||||
# mandir = "/usr/share/man"
|
||||
# infodir = "/usr/share/info"
|
||||
|
||||
# Additional examples:
|
||||
# makeflags = "-j8"
|
||||
# env_vars = ["CCACHE_DIR=/var/cache/ccache"]
|
||||
# passthrough_env = ["http_proxy", "https_proxy"]
|
||||
|
||||
[install]
|
||||
# Install test dependencies along with build/runtime dependencies.
|
||||
# When true, Depot will also try to install declared test dependencies while
|
||||
# resolving dependencies for source builds.
|
||||
test_deps = false
|
||||
|
||||
# Example future-friendly place for install policy overrides:
|
||||
# assume_yes = false
|
||||
# keep_cache = true
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
# Example per-user configuration (~/.config/depot.toml)
|
||||
#
|
||||
# Use this file for local preferences that should not affect other users on the
|
||||
# same machine. System-wide defaults still come from /etc/depot.toml and
|
||||
# /etc/depot.d/*.toml unless you override them here.
|
||||
|
||||
[build]
|
||||
# A user-local prefix is useful when you are building software for your own
|
||||
# account instead of assembling system packages.
|
||||
prefix = "/usr/local"
|
||||
|
||||
# Personal toolchain preference.
|
||||
cc = "clang"
|
||||
# cxx = "clang++"
|
||||
|
||||
[build.flags]
|
||||
# User-local compiler preferences.
|
||||
cflags = ["-O2"]
|
||||
cflags += ["-g"]
|
||||
carch = "x86_64"
|
||||
|
||||
[install]
|
||||
test_deps = false
|
||||
# Examples:
|
||||
# cxxflags = ["-O2", "-g"]
|
||||
# ldflags = ["-Wl,-O1"]
|
||||
# makeflags = "-j4"
|
||||
|
||||
# Optional install directory overrides for supported builders
|
||||
#bindir = "/usr/local/bin"
|
||||
#sbindir = "/usr/local/sbin"
|
||||
#libdir = "/usr/local/lib"
|
||||
# Optional install directory overrides for supported builders.
|
||||
# bindir = "/usr/local/bin"
|
||||
# sbindir = "/usr/local/sbin"
|
||||
# libdir = "/usr/local/lib"
|
||||
# datadir = "/usr/local/share"
|
||||
|
||||
[install]
|
||||
# Install test dependencies automatically for your own source builds.
|
||||
test_deps = false
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
project(
|
||||
'depot',
|
||||
version: '0.35.0',
|
||||
version: '0.35.1',
|
||||
meson_version: '>=0.60.0',
|
||||
default_options: ['buildtype=release'],
|
||||
)
|
||||
@@ -21,6 +21,7 @@ depot_custom_package = get_option('DEPOT_CUSTOM_PACKAGE')
|
||||
depot_python_package = get_option('DEPOT_PYTHON_PACKAGE')
|
||||
depot_rust_package = get_option('DEPOT_RUST_PACKAGE')
|
||||
depot_makefile_package = get_option('DEPOT_MAKEFILE_PACKAGE')
|
||||
depot_development_package = get_option('DEPOT_DEVELOPMENT_PACKAGE')
|
||||
|
||||
src_root = meson.project_source_root()
|
||||
build_root = meson.project_build_root()
|
||||
@@ -84,6 +85,7 @@ depot_bin = custom_target(
|
||||
depot_python_package,
|
||||
depot_rust_package,
|
||||
depot_makefile_package,
|
||||
depot_development_package,
|
||||
'@OUTPUT@',
|
||||
],
|
||||
console: true,
|
||||
@@ -128,6 +130,7 @@ test(
|
||||
depot_python_package,
|
||||
depot_rust_package,
|
||||
depot_makefile_package,
|
||||
depot_development_package,
|
||||
],
|
||||
suite: ['cargo'],
|
||||
timeout: 0,
|
||||
|
||||
@@ -58,3 +58,9 @@ option(
|
||||
value: '',
|
||||
description: 'Compile in the helper package to auto-install before makefile builds',
|
||||
)
|
||||
option(
|
||||
'DEPOT_DEVELOPMENT_PACKAGE',
|
||||
type: 'string',
|
||||
value: 'development-base',
|
||||
description: 'Compile in the package that must already be installed before any source build runs',
|
||||
)
|
||||
|
||||
+18
-1
@@ -2,6 +2,7 @@ use crate::package::BuildType;
|
||||
use anyhow::Result;
|
||||
|
||||
const BUILD_DEPOT_STATIC_OPTION: &str = "BUILD_DEPOT_STATIC";
|
||||
const DEPOT_DEVELOPMENT_PACKAGE_OPTION: &str = "DEPOT_DEVELOPMENT_PACKAGE";
|
||||
|
||||
fn parse_boolish_option(value: &str) -> Option<bool> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
@@ -63,9 +64,20 @@ pub(crate) fn requested_build_tool_package(build_type: BuildType) -> Option<Stri
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn development_package_option() -> &'static str {
|
||||
DEPOT_DEVELOPMENT_PACKAGE_OPTION
|
||||
}
|
||||
|
||||
pub(crate) fn requested_development_package() -> Option<String> {
|
||||
normalize_string_option(option_env!("DEPOT_DEVELOPMENT_PACKAGE"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{build_tool_package_option, normalize_string_option, parse_boolish_option};
|
||||
use super::{
|
||||
build_tool_package_option, development_package_option, normalize_string_option,
|
||||
parse_boolish_option,
|
||||
};
|
||||
use crate::package::BuildType;
|
||||
|
||||
#[test]
|
||||
@@ -99,4 +111,9 @@ mod tests {
|
||||
);
|
||||
assert_eq!(build_tool_package_option(BuildType::Bin), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn development_package_option_matches_expected_name() {
|
||||
assert_eq!(development_package_option(), "DEPOT_DEVELOPMENT_PACKAGE");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,9 @@ pub fn build(
|
||||
let expanded = expand_configure_arg(spec, arg, &env_vars);
|
||||
configure_cmd.arg(expanded);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Autotools)? {
|
||||
for arg in
|
||||
crate::builder::static_build_args_for(crate::package::BuildType::Autotools, flags)?
|
||||
{
|
||||
add_auto_configure_arg_if_supported(&mut configure_cmd, help_text.as_deref(), &arg);
|
||||
}
|
||||
|
||||
@@ -444,7 +446,9 @@ pub(crate) fn ensure_host_build(
|
||||
let expanded = expand_configure_arg(&host_spec, arg, &env_vars);
|
||||
configure_cmd.arg(expanded);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Autotools)? {
|
||||
for arg in
|
||||
crate::builder::static_build_args_for(crate::package::BuildType::Autotools, flags)?
|
||||
{
|
||||
add_auto_configure_arg_if_supported(&mut configure_cmd, help_text.as_deref(), &arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ pub fn build(
|
||||
let expanded = expand_with_envs(flag, &env_vars);
|
||||
cmake_cmd.arg(&expanded);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake)? {
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake, flags)? {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ pub(crate) fn ensure_host_build(
|
||||
let expanded = expand_with_envs(flag, &env_vars);
|
||||
cmake_cmd.arg(&expanded);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake)? {
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake, flags)? {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
|
||||
|
||||
+187
-32
@@ -8,6 +8,7 @@ use anyhow::{Context, Result};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
pub fn build(
|
||||
spec: &PackageSpec,
|
||||
@@ -98,15 +99,6 @@ pub fn build(
|
||||
}
|
||||
|
||||
if !state.is_done(BuildStep::PostInstallDone) {
|
||||
crate::log_info!(
|
||||
"Running custom build script{}...",
|
||||
if fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
}
|
||||
);
|
||||
|
||||
crate::builder::set_env_var(
|
||||
&mut env_vars,
|
||||
"DESTDIR",
|
||||
@@ -139,13 +131,31 @@ pub fn build(
|
||||
std::env::current_dir()?.join(&build_script)
|
||||
};
|
||||
|
||||
// Use POSIX `sh` (more likely to be available in minimal/chroot environments)
|
||||
let mut cmd = if custom_function_mode_enabled(&abs_build_script)? {
|
||||
let function_mode = custom_function_mode_enabled(&abs_build_script)?;
|
||||
if function_mode {
|
||||
crate::log_info!(
|
||||
"Running custom build script (function mode; fakeroot only during install)..."
|
||||
);
|
||||
crate::log_info!(
|
||||
"Using custom build.sh function mode (per-output install functions enabled)"
|
||||
);
|
||||
build_function_mode_command(spec, &install_destdir, &abs_build_script)?
|
||||
run_function_mode_build_script(
|
||||
spec,
|
||||
&build_dir,
|
||||
&install_destdir,
|
||||
&abs_build_script,
|
||||
&env_vars,
|
||||
)?;
|
||||
} else {
|
||||
crate::log_info!(
|
||||
"Running custom build script{}...",
|
||||
if fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
}
|
||||
);
|
||||
// Use POSIX `sh` (doing something wrong if your system doesn't have it...)
|
||||
let mut cmd = fakeroot::wrap_install_command("sh", &install_destdir);
|
||||
let wrapper = crate::shell_helpers::wrap_shell_command(". \"$1\"");
|
||||
// Run custom scripts through `sh -c` so helper commands like `haul`
|
||||
@@ -155,23 +165,22 @@ pub fn build(
|
||||
.arg(wrapper)
|
||||
.arg("sh")
|
||||
.arg(&abs_build_script);
|
||||
cmd
|
||||
};
|
||||
cmd.current_dir(&build_dir);
|
||||
cmd.current_dir(&build_dir);
|
||||
|
||||
crate::builder::prepare_tool_command(&mut cmd, &env_vars);
|
||||
crate::builder::prepare_tool_command(&mut cmd, &env_vars);
|
||||
|
||||
// Run the command and include the OS error on spawn failures for clearer diagnostics
|
||||
let status = crate::interrupts::command_status(&mut cmd).map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to run build script {}: {}",
|
||||
build_script.display(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
// Run the command and include the OS error on spawn failures for clearer diagnostics
|
||||
let status = crate::interrupts::command_status(&mut cmd).map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to run build script {}: {}",
|
||||
build_script.display(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("Custom build script failed with status: {}", status);
|
||||
if !status.success() {
|
||||
anyhow::bail!("Custom build script failed with status: {}", status);
|
||||
}
|
||||
}
|
||||
if flags.lib32_variant {
|
||||
crate::builder::stage_lib32_install_tree(&install_destdir, destdir)?;
|
||||
@@ -213,11 +222,7 @@ fn custom_function_mode_enabled(build_script: &Path) -> Result<bool> {
|
||||
|| contents.contains("install_"))
|
||||
}
|
||||
|
||||
fn build_function_mode_command(
|
||||
spec: &PackageSpec,
|
||||
destdir: &Path,
|
||||
build_script: &Path,
|
||||
) -> Result<Command> {
|
||||
fn function_mode_shell_prelude() -> String {
|
||||
let mut wrapper = crate::shell_helpers::wrap_shell_command("");
|
||||
wrapper.push_str("\nset -eu\n");
|
||||
wrapper.push_str("depot_has_function() {\n");
|
||||
@@ -226,11 +231,39 @@ fn build_function_mode_command(
|
||||
wrapper.push_str(" *) return 1 ;;\n");
|
||||
wrapper.push_str(" esac\n");
|
||||
wrapper.push_str("}\n");
|
||||
wrapper
|
||||
}
|
||||
|
||||
fn build_function_mode_build_command(build_script: &Path, state_file: &Path) -> Command {
|
||||
let mut wrapper = function_mode_shell_prelude();
|
||||
wrapper.push_str("depot_build_ran=0\n");
|
||||
wrapper.push_str(". \"$1\"\n");
|
||||
wrapper.push_str("if depot_has_function depot_build; then depot_build; depot_build_ran=1;\n");
|
||||
wrapper.push_str("elif depot_has_function build; then build; depot_build_ran=1;\n");
|
||||
wrapper.push_str("fi\n");
|
||||
wrapper.push_str("printf '%s\\n' \"$depot_build_ran\" > \"$2\"\n");
|
||||
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.arg("-c")
|
||||
.arg(wrapper)
|
||||
.arg("sh")
|
||||
.arg(build_script)
|
||||
.arg(state_file);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn build_function_mode_install_command(
|
||||
spec: &PackageSpec,
|
||||
destdir: &Path,
|
||||
build_script: &Path,
|
||||
build_ran: bool,
|
||||
) -> Command {
|
||||
let mut wrapper = function_mode_shell_prelude();
|
||||
wrapper.push_str(&format!(
|
||||
"depot_build_ran={}\n",
|
||||
if build_ran { 1 } else { 0 }
|
||||
));
|
||||
wrapper.push_str(". \"$1\"\n");
|
||||
|
||||
let primary = &spec.package.name;
|
||||
for out in spec.outputs() {
|
||||
@@ -272,7 +305,65 @@ fn build_function_mode_command(
|
||||
|
||||
let mut cmd = fakeroot::wrap_install_command("sh", destdir);
|
||||
cmd.arg("-c").arg(wrapper).arg("sh").arg(build_script);
|
||||
Ok(cmd)
|
||||
cmd
|
||||
}
|
||||
|
||||
fn run_function_mode_build_script(
|
||||
spec: &PackageSpec,
|
||||
build_dir: &Path,
|
||||
install_destdir: &Path,
|
||||
build_script: &Path,
|
||||
env_vars: &crate::builder::EnvVars,
|
||||
) -> Result<()> {
|
||||
let state_file = NamedTempFile::new_in(build_dir).with_context(|| {
|
||||
format!(
|
||||
"Failed to create function mode state file in {}",
|
||||
build_dir.display()
|
||||
)
|
||||
})?;
|
||||
let state_path = state_file.path().to_path_buf();
|
||||
|
||||
let mut build_cmd = build_function_mode_build_command(build_script, &state_path);
|
||||
build_cmd.current_dir(build_dir);
|
||||
let mut build_env = env_vars.clone();
|
||||
crate::builder::set_env_var(
|
||||
&mut build_env,
|
||||
"DESTDIR",
|
||||
install_destdir.to_string_lossy().into_owned(),
|
||||
);
|
||||
crate::builder::prepare_tool_command(&mut build_cmd, &build_env);
|
||||
let build_status = crate::interrupts::command_status(&mut build_cmd).map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to run build script {}: {}",
|
||||
build_script.display(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
if !build_status.success() {
|
||||
anyhow::bail!("Custom build script failed with status: {}", build_status);
|
||||
}
|
||||
|
||||
let build_ran = fs::read_to_string(&state_path)
|
||||
.ok()
|
||||
.map(|value| value.trim() == "1")
|
||||
.unwrap_or(false);
|
||||
|
||||
let mut install_cmd =
|
||||
build_function_mode_install_command(spec, install_destdir, build_script, build_ran);
|
||||
install_cmd.current_dir(build_dir);
|
||||
crate::builder::prepare_tool_command(&mut install_cmd, env_vars);
|
||||
let install_status = crate::interrupts::command_status(&mut install_cmd).map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to run build script {}: {}",
|
||||
build_script.display(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
if !install_status.success() {
|
||||
anyhow::bail!("Custom build script failed with status: {}", install_status);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn shell_fn_suffix(pkg_name: &str) -> String {
|
||||
@@ -430,6 +521,70 @@ depot_install_dev_pkg() {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_function_mode_only_uses_fakeroot_for_install() -> Result<()> {
|
||||
let tmp_src = tempdir()?;
|
||||
let tmp_dest = tempdir()?;
|
||||
|
||||
let build_sh = tmp_src.path().join("build.sh");
|
||||
std::fs::write(
|
||||
&build_sh,
|
||||
r#"#!/bin/sh
|
||||
depot_build() {
|
||||
if [ "${FAKEROOT_ACTIVE:-0}" = 1 ]; then
|
||||
echo yes > build-fakeroot.txt
|
||||
else
|
||||
echo no > build-fakeroot.txt
|
||||
fi
|
||||
}
|
||||
depot_install() {
|
||||
mkdir -p "$DESTDIR/usr/share"
|
||||
echo installed > "$DESTDIR/usr/share/install-fakeroot.txt"
|
||||
}
|
||||
"#,
|
||||
)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
std::fs::set_permissions(&build_sh, perms)?;
|
||||
}
|
||||
|
||||
let spec = mk_spec("custom-function-fakeroot-split", "1.0");
|
||||
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(tmp_src.path().join("build-fakeroot.txt"))?,
|
||||
"no\n"
|
||||
);
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(tmp_dest.path().join("usr/share/install-fakeroot.txt"))?,
|
||||
"installed\n"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_function_mode_commands_split_fakeroot_boundary() {
|
||||
let build_script = Path::new("/tmp/build.sh");
|
||||
let state_file = Path::new("/tmp/build-state");
|
||||
let install_destdir = Path::new("/tmp/destdir");
|
||||
|
||||
let build_cmd = build_function_mode_build_command(build_script, state_file);
|
||||
assert_eq!(build_cmd.get_program(), std::ffi::OsStr::new("sh"));
|
||||
|
||||
let spec = mk_spec("custom-function-fakeroot-split", "1.0");
|
||||
let install_cmd =
|
||||
build_function_mode_install_command(&spec, install_destdir, build_script, true);
|
||||
let expected = if crate::fakeroot::is_root() {
|
||||
std::ffi::OsStr::new("sh")
|
||||
} else {
|
||||
std::ffi::OsStr::new("fakeroot")
|
||||
};
|
||||
assert_eq!(install_cmd.get_program(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_function_mode_errors_when_output_handler_missing() -> Result<()> {
|
||||
let tmp_src = tempdir()?;
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn build(
|
||||
for arg in meson_setup_args(flags, cross_file.as_deref(), &env_vars) {
|
||||
meson_cmd.arg(arg);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson)? {
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson, flags)? {
|
||||
meson_cmd.arg(arg);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ pub(crate) fn ensure_host_build(
|
||||
for arg in meson_setup_args(flags, None, &env_vars) {
|
||||
meson_cmd.arg(arg);
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson)? {
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson, flags)? {
|
||||
meson_cmd.arg(arg);
|
||||
}
|
||||
|
||||
|
||||
+53
-7
@@ -12,7 +12,7 @@ mod rust;
|
||||
pub mod state;
|
||||
|
||||
use crate::cross::CrossConfig;
|
||||
use crate::package::{BuildType, PackageSpec};
|
||||
use crate::package::{BuildFlags, BuildType, PackageSpec};
|
||||
use anyhow::{Context, Result};
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
@@ -188,12 +188,20 @@ pub(crate) fn requested_static_build() -> Result<Option<bool>> {
|
||||
crate::build_options::requested_static_build()
|
||||
}
|
||||
|
||||
pub(crate) fn static_build_args_for(build_type: BuildType) -> Result<Vec<String>> {
|
||||
let Some(enabled) = requested_static_build()? else {
|
||||
return Ok(Vec::new());
|
||||
fn static_build_args_for_request(
|
||||
build_type: BuildType,
|
||||
requested_static: Option<bool>,
|
||||
no_delete_static: bool,
|
||||
) -> Vec<String> {
|
||||
let Some(enabled) = requested_static else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let args = match build_type {
|
||||
if !enabled && no_delete_static {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
match build_type {
|
||||
BuildType::Autotools => vec![if enabled {
|
||||
"--enable-static".to_string()
|
||||
} else {
|
||||
@@ -212,9 +220,18 @@ pub(crate) fn static_build_args_for(build_type: BuildType) -> Result<Vec<String>
|
||||
if enabled { "static" } else { "dynamic" }
|
||||
)],
|
||||
_ => Vec::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
pub(crate) fn static_build_args_for(
|
||||
build_type: BuildType,
|
||||
flags: &BuildFlags,
|
||||
) -> Result<Vec<String>> {
|
||||
Ok(static_build_args_for_request(
|
||||
build_type,
|
||||
requested_static_build()?,
|
||||
flags.no_delete_static,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn build_tool_package_option(build_type: BuildType) -> Option<&'static str> {
|
||||
@@ -225,6 +242,14 @@ pub(crate) fn requested_build_tool_package(build_type: BuildType) -> Option<Stri
|
||||
crate::build_options::requested_build_tool_package(build_type)
|
||||
}
|
||||
|
||||
pub(crate) fn development_package_option() -> &'static str {
|
||||
crate::build_options::development_package_option()
|
||||
}
|
||||
|
||||
pub(crate) fn requested_development_package() -> Option<String> {
|
||||
crate::build_options::requested_development_package()
|
||||
}
|
||||
|
||||
fn configured_install_dir(value: &str, default: &str) -> String {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
@@ -943,6 +968,27 @@ mod tests {
|
||||
assert_eq!(build_tool_package_option(BuildType::Bin), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_build_args_skip_disable_static_when_no_delete_static_enabled() {
|
||||
let args = static_build_args_for_request(BuildType::Autotools, Some(false), true);
|
||||
assert!(args.is_empty());
|
||||
|
||||
let args = static_build_args_for_request(BuildType::CMake, Some(false), true);
|
||||
assert!(args.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_build_args_keep_other_requested_modes() {
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Autotools, Some(false), false),
|
||||
vec!["--disable-static".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Meson, Some(true), true),
|
||||
vec!["-Ddefault_library=static".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standard_build_env_exports_native_linker_and_cpp() {
|
||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ pub fn build(
|
||||
for arg in &flags.configure {
|
||||
configure_cmd.arg(spec.expand_vars(arg));
|
||||
}
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Perl)? {
|
||||
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Perl, flags)? {
|
||||
configure_cmd.arg(arg);
|
||||
}
|
||||
crate::builder::prepare_tool_command(&mut configure_cmd, &env_vars);
|
||||
|
||||
+95
-18
@@ -20,9 +20,9 @@ use walkdir::WalkDir;
|
||||
|
||||
use build_cmd::support::{
|
||||
automatic_tests_disabled_for_outputs, build_lib32_companion_package, clean_build_workspace,
|
||||
effective_lib32_only, make_lib32_package_spec, maybe_disable_tests_for_missing_deps,
|
||||
maybe_prompt_to_skip_tests_for_missing_requested_deps, merge_missing_dependencies,
|
||||
requested_outputs, should_install_test_deps,
|
||||
effective_lib32_only, ensure_requested_development_package_installed, make_lib32_package_spec,
|
||||
maybe_disable_tests_for_missing_deps, maybe_prompt_to_skip_tests_for_missing_requested_deps,
|
||||
merge_missing_dependencies, requested_outputs, should_install_test_deps,
|
||||
};
|
||||
use install_cmd::archive::{
|
||||
extract_package_archive_to_staging, load_package_archive_into_staging,
|
||||
@@ -1420,15 +1420,88 @@ fn actionable_plan_packages(plan: &planner::ExecutionPlan) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn warn_source_build_installs(count: usize) {
|
||||
if count > 0 {
|
||||
ui::warn(format!(
|
||||
"{} package(s) will be built from source before installation.",
|
||||
count
|
||||
));
|
||||
fn source_build_reason(reason: &str) -> String {
|
||||
if let Some(dep) = reason.strip_prefix("dependency ") {
|
||||
format!("requested dependency '{dep}'")
|
||||
} else if let Some((requester, _)) = reason.split_once(" needs ") {
|
||||
format!("needed by '{requester}'")
|
||||
} else if reason == "requested spec" {
|
||||
"requested spec".to_string()
|
||||
} else if reason == "requested package" {
|
||||
"requested package".to_string()
|
||||
} else {
|
||||
reason.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn source_build_warning_messages(plan: &planner::ExecutionPlan) -> Vec<String> {
|
||||
let mut lines = Vec::new();
|
||||
for step in plan.actionable_steps() {
|
||||
if !matches!(step.action, planner::PlanAction::BuildAndInstall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut reasons = Vec::new();
|
||||
for reason in &step.requested_by {
|
||||
let label = source_build_reason(reason);
|
||||
if !reasons.contains(&label) {
|
||||
reasons.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
if reasons.is_empty() {
|
||||
lines.push(step.package.clone());
|
||||
} else {
|
||||
lines.push(format!("{} ({})", step.package, reasons.join(", ")));
|
||||
}
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
fn warn_source_build_plan(plan: &planner::ExecutionPlan) {
|
||||
let lines = source_build_warning_messages(plan);
|
||||
if lines.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
ui::warn(format!(
|
||||
"{} package(s) will be built from source before installation.",
|
||||
lines.len()
|
||||
));
|
||||
for line in lines {
|
||||
ui::warn(format!(" {line}"));
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_source_build_prereqs_for_plan(
|
||||
plan: &planner::ExecutionPlan,
|
||||
rootfs: &Path,
|
||||
config: &config::Config,
|
||||
) -> Result<()> {
|
||||
let db_path = config.installed_db_path(rootfs);
|
||||
let mut checked_development_package = false;
|
||||
|
||||
for step in plan.actionable_steps() {
|
||||
let planner::PlanOrigin::Source { path, .. } = &step.origin else {
|
||||
continue;
|
||||
};
|
||||
if !matches!(step.action, planner::PlanAction::BuildAndInstall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut spec = package::PackageSpec::from_file(path)
|
||||
.with_context(|| format!("Failed to parse spec {}", path.display()))?;
|
||||
spec.apply_config(config);
|
||||
source::preflight_local_manual_sources(&spec)?;
|
||||
if !checked_development_package && !spec.is_metapackage() {
|
||||
ensure_requested_development_package_installed(&db_path)?;
|
||||
checked_development_package = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct InstallPlanExecutionOptions<'a> {
|
||||
no_flags: bool,
|
||||
@@ -1532,14 +1605,14 @@ fn execute_install_plan_with_child_commands(
|
||||
record: db::repo::BinaryRepoPackageRecord,
|
||||
}
|
||||
|
||||
let summary = plan.summary();
|
||||
let actionable_steps: Vec<_> = plan.actionable_steps().collect();
|
||||
if actionable_steps.is_empty() {
|
||||
ui::info("Nothing to do.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
warn_source_build_installs(summary.source_build_installs);
|
||||
validate_source_build_prereqs_for_plan(plan, rootfs, config)?;
|
||||
warn_source_build_plan(plan);
|
||||
let planned_packages = actionable_plan_packages(plan);
|
||||
if options.confirm_installation
|
||||
&& !ui::prompt_package_action("installation", &planned_packages, true)?
|
||||
@@ -2083,6 +2156,14 @@ fn run_direct_install_request(
|
||||
}
|
||||
|
||||
let requested_outputs = requested_outputs(&pkg_spec, lib32_only);
|
||||
let db_path = config.installed_db_path(options.rootfs);
|
||||
|
||||
if staging_dir.is_none() {
|
||||
source::preflight_local_manual_sources(&pkg_spec)?;
|
||||
if !pkg_spec.is_metapackage() {
|
||||
ensure_requested_development_package_installed(&db_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut conflict_subjects = install_conflict_subjects_for_spec(
|
||||
&pkg_spec,
|
||||
@@ -2122,11 +2203,6 @@ fn run_direct_install_request(
|
||||
config.db_dir.display()
|
||||
)
|
||||
})?;
|
||||
let db_path = config.installed_db_path(options.rootfs);
|
||||
|
||||
if staging_dir.is_none() {
|
||||
source::preflight_manual_sources(&pkg_spec, &config.cache_dir)?;
|
||||
}
|
||||
|
||||
if staging_dir.is_none() {
|
||||
if options.no_deps
|
||||
@@ -2190,7 +2266,7 @@ fn run_direct_install_request(
|
||||
},
|
||||
)?;
|
||||
let dep_plan_packages = actionable_plan_packages(&dep_plan);
|
||||
warn_source_build_installs(dep_plan.summary().source_build_installs);
|
||||
warn_source_build_plan(&dep_plan);
|
||||
let dep_prompt_packages = if dep_plan_packages.is_empty() {
|
||||
missing_required.clone()
|
||||
} else {
|
||||
@@ -2293,7 +2369,7 @@ fn run_direct_install_request(
|
||||
},
|
||||
)?;
|
||||
let dep_plan_packages = actionable_plan_packages(&dep_plan);
|
||||
warn_source_build_installs(dep_plan.summary().source_build_installs);
|
||||
warn_source_build_plan(&dep_plan);
|
||||
let dep_prompt_packages = if dep_plan_packages.is_empty() {
|
||||
missing_test.clone()
|
||||
} else {
|
||||
@@ -2362,6 +2438,7 @@ fn run_direct_install_request(
|
||||
dir.path().to_path_buf()
|
||||
} else {
|
||||
// 1-2. Fetch + extract sources (supports archives and git URL#rev)
|
||||
source::preflight_manual_sources(&pkg_spec, &config.cache_dir)?;
|
||||
let src_dir = source::prepare(&pkg_spec, &config.cache_dir, &config.build_dir)?;
|
||||
built_src_dir = Some(src_dir.clone());
|
||||
let host_build_dir = builder::ensure_host_build(
|
||||
|
||||
@@ -5,9 +5,10 @@ pub(crate) mod support;
|
||||
use self::support::{
|
||||
RequestedBuildToolPackageInstall, automatic_tests_disabled_for_outputs,
|
||||
build_lib32_companion_package, clean_build_workspace, effective_lib32_only,
|
||||
ensure_requested_build_tool_package_installed, maybe_disable_tests_for_missing_deps,
|
||||
maybe_prompt_to_skip_tests_for_missing_requested_deps, merge_missing_dependencies,
|
||||
requested_outputs, should_install_test_deps, warn_if_running_as_root_for_build,
|
||||
ensure_requested_build_tool_package_installed, ensure_requested_development_package_installed,
|
||||
maybe_disable_tests_for_missing_deps, maybe_prompt_to_skip_tests_for_missing_requested_deps,
|
||||
merge_missing_dependencies, requested_outputs, should_install_test_deps,
|
||||
warn_if_running_as_root_for_build,
|
||||
};
|
||||
|
||||
pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> {
|
||||
@@ -47,6 +48,12 @@ pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> {
|
||||
pkg_spec.apply_config(&config);
|
||||
let lib32_only = effective_lib32_only(&pkg_spec, cli_lib32_only);
|
||||
let requested_outputs = requested_outputs(&pkg_spec, lib32_only);
|
||||
let db_path = config.installed_db_path(&rootfs);
|
||||
|
||||
source::preflight_local_manual_sources(&pkg_spec)?;
|
||||
if !pkg_spec.is_metapackage() {
|
||||
ensure_requested_development_package_installed(&db_path)?;
|
||||
}
|
||||
|
||||
let build_targets = vec![format!(
|
||||
"{} v{}-{}",
|
||||
@@ -62,7 +69,6 @@ pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> {
|
||||
config.db_dir.display()
|
||||
)
|
||||
})?;
|
||||
let db_path = config.installed_db_path(&rootfs);
|
||||
|
||||
if no_deps && should_install_test_deps(&pkg_spec, install_test_deps, requested_outputs) {
|
||||
let missing_test =
|
||||
@@ -297,6 +303,7 @@ pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> {
|
||||
watcher.check()?;
|
||||
}
|
||||
|
||||
source::preflight_manual_sources(&pkg_spec, &config.cache_dir)?;
|
||||
let src_dir = source::prepare(&pkg_spec, &config.cache_dir, &config.build_dir)?;
|
||||
if let Some(watcher) = interrupt_watcher.as_ref() {
|
||||
watcher.check()?;
|
||||
|
||||
@@ -247,6 +247,30 @@ pub(crate) fn ensure_requested_build_tool_package_installed(
|
||||
)
|
||||
}
|
||||
|
||||
fn ensure_requested_development_package_installed_for(
|
||||
package_name: Option<&str>,
|
||||
db_path: &Path,
|
||||
) -> Result<()> {
|
||||
let Some(package_name) = package_name.filter(|name| !name.trim().is_empty()) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if deps::is_dep_satisfied_in_db(package_name, db_path)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
anyhow::bail!(
|
||||
"Missing required development package for source builds ({}): {}. Install it first before building packages from source.",
|
||||
builder::development_package_option(),
|
||||
package_name
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_requested_development_package_installed(db_path: &Path) -> Result<()> {
|
||||
let package_name = builder::requested_development_package();
|
||||
ensure_requested_development_package_installed_for(package_name.as_deref(), db_path)
|
||||
}
|
||||
|
||||
pub(crate) fn build_lib32_companion_package(
|
||||
pkg_spec: &package::PackageSpec,
|
||||
src_dir: &Path,
|
||||
@@ -323,3 +347,77 @@ pub(crate) fn build_lib32_companion_package(
|
||||
|
||||
Ok(Some((lib32_pkg_spec, lib32_destdir)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ensure_requested_development_package_installed_for;
|
||||
use crate::config;
|
||||
use crate::db;
|
||||
use crate::package::{
|
||||
Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec, Source,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn test_spec(name: &str) -> PackageSpec {
|
||||
PackageSpec {
|
||||
package: PackageInfo {
|
||||
name: name.into(),
|
||||
real_name: None,
|
||||
version: "1.0.0".into(),
|
||||
revision: 1,
|
||||
description: "d".into(),
|
||||
homepage: "https://example.test".into(),
|
||||
abi_breaking: false,
|
||||
license: vec!["MIT".into()],
|
||||
},
|
||||
packages: Vec::new(),
|
||||
alternatives: Default::default(),
|
||||
manual_sources: Vec::new(),
|
||||
source: vec![Source {
|
||||
url: "https://example.test/src.tar.gz".into(),
|
||||
sha256: "skip".into(),
|
||||
extract_dir: "src".into(),
|
||||
patches: Vec::new(),
|
||||
post_extract: Vec::new(),
|
||||
cherry_pick: Vec::new(),
|
||||
}],
|
||||
build: Build {
|
||||
build_type: BuildType::Custom,
|
||||
flags: BuildFlags::default(),
|
||||
},
|
||||
dependencies: Dependencies::default(),
|
||||
package_alternatives: Default::default(),
|
||||
package_dependencies: Default::default(),
|
||||
spec_dir: PathBuf::from("."),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requested_development_package_requirement_fails_when_missing() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let db_path = temp.path().join("installed.sqlite");
|
||||
let err =
|
||||
ensure_requested_development_package_installed_for(Some("development-base"), &db_path)
|
||||
.expect_err("missing development package should fail");
|
||||
assert!(err.to_string().contains("development-base"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requested_development_package_requirement_passes_when_installed() -> Result<()> {
|
||||
let rootfs = tempfile::tempdir()?;
|
||||
let config = config::Config::for_rootfs(rootfs.path());
|
||||
fs::create_dir_all(&config.db_dir)?;
|
||||
let db_path = config.installed_db_path(rootfs.path());
|
||||
|
||||
let spec = test_spec("development-base");
|
||||
let dest = rootfs.path().join("dest").join("development-base");
|
||||
fs::create_dir_all(dest.join("usr/bin"))?;
|
||||
fs::write(dest.join("usr/bin/dev-base"), "bin")?;
|
||||
db::register_package(&db_path, &spec, &dest)?;
|
||||
|
||||
ensure_requested_development_package_installed_for(Some("development-base"), &db_path)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2214,6 +2214,159 @@ file = "missing.patch"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_command_checks_manual_sources_before_dependency_resolution() -> Result<()> {
|
||||
let _guard = ASSUME_YES_TEST_LOCK.lock().expect("assume-yes test lock");
|
||||
let temp = tempfile::tempdir().context("Failed to create temp dir")?;
|
||||
let rootfs = temp.path().join("rootfs");
|
||||
let spec_dir = temp.path().join("packages").join("demo");
|
||||
fs::create_dir_all(&rootfs)?;
|
||||
fs::create_dir_all(&spec_dir)?;
|
||||
|
||||
let spec_path = spec_dir.join("demo.toml");
|
||||
fs::write(
|
||||
&spec_path,
|
||||
r#"[package]
|
||||
name = "demo"
|
||||
version = "1.0.0"
|
||||
revision = 1
|
||||
description = "demo"
|
||||
homepage = "https://example.test/demo"
|
||||
license = "MIT"
|
||||
|
||||
[[source]]
|
||||
url = "https://example.test/demo-1.0.0.tar.gz"
|
||||
sha256 = "skip"
|
||||
extract_dir = "demo-1.0.0"
|
||||
|
||||
[build]
|
||||
type = "custom"
|
||||
|
||||
[dependencies]
|
||||
build = ["definitely-missing-dep"]
|
||||
runtime = []
|
||||
optional = []
|
||||
|
||||
[[manual_sources]]
|
||||
file = "missing.patch"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let result = run(Cli {
|
||||
command: Commands::Build(BuildArgs {
|
||||
rootfs_args: rootfs_args(rootfs),
|
||||
prompt_args: prompt_args(true),
|
||||
build_exec_args: build_exec_args(),
|
||||
lib32_args: lib32_args(),
|
||||
spec_pos: Some(spec_path),
|
||||
spec: None,
|
||||
install: false,
|
||||
install_deps: true,
|
||||
cleanup_deps: false,
|
||||
}),
|
||||
});
|
||||
|
||||
let err = result.expect_err("missing manual source should fail before dependency install");
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("Manual source not found: missing.patch")
|
||||
);
|
||||
assert!(
|
||||
!err.to_string()
|
||||
.contains("Failed to resolve required build tool package")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_build_warning_messages_include_dependency_context() {
|
||||
let plan = planner::ExecutionPlan {
|
||||
steps: vec![
|
||||
planner::PlannedStep {
|
||||
package: "dep-src".into(),
|
||||
action: planner::PlanAction::BuildAndInstall,
|
||||
origin: planner::PlanOrigin::Source {
|
||||
path: PathBuf::from("/tmp/dep-src.toml"),
|
||||
local_sibling: false,
|
||||
},
|
||||
requested_by: vec!["dependency dep-src".into(), "app needs dep-src".into()],
|
||||
},
|
||||
planner::PlannedStep {
|
||||
package: "dep-bin".into(),
|
||||
action: planner::PlanAction::InstallBinary,
|
||||
origin: planner::PlanOrigin::Binary {
|
||||
repo_name: "core".into(),
|
||||
record: Box::new(test_binary_repo_record(
|
||||
"dep-bin",
|
||||
"dep-bin-1.0-1-x86_64.tar.zst",
|
||||
)),
|
||||
},
|
||||
requested_by: vec!["app needs dep-bin".into()],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
source_build_warning_messages(&plan),
|
||||
vec!["dep-src (requested dependency 'dep-src', needed by 'app')".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn planned_source_build_prereqs_check_manual_sources_before_confirmation() -> Result<()> {
|
||||
let rootfs = tempfile::tempdir().context("Failed to create temp rootfs")?;
|
||||
let spec_dir = tempfile::tempdir().context("Failed to create temp spec dir")?;
|
||||
let spec_path = spec_dir.path().join("demo.toml");
|
||||
fs::write(
|
||||
&spec_path,
|
||||
r#"[package]
|
||||
name = "demo"
|
||||
version = "1.0.0"
|
||||
revision = 1
|
||||
description = "demo"
|
||||
homepage = "https://example.test/demo"
|
||||
license = "MIT"
|
||||
|
||||
[[source]]
|
||||
url = "https://example.test/demo-1.0.0.tar.gz"
|
||||
sha256 = "skip"
|
||||
extract_dir = "demo-1.0.0"
|
||||
|
||||
[build]
|
||||
type = "custom"
|
||||
|
||||
[dependencies]
|
||||
build = []
|
||||
runtime = []
|
||||
optional = []
|
||||
|
||||
[[manual_sources]]
|
||||
file = "missing.patch"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = config::Config::for_rootfs(rootfs.path());
|
||||
let plan = planner::ExecutionPlan {
|
||||
steps: vec![planner::PlannedStep {
|
||||
package: "demo".into(),
|
||||
action: planner::PlanAction::BuildAndInstall,
|
||||
origin: planner::PlanOrigin::Source {
|
||||
path: spec_path,
|
||||
local_sibling: true,
|
||||
},
|
||||
requested_by: vec!["requested spec".into()],
|
||||
}],
|
||||
};
|
||||
|
||||
let err = validate_source_build_prereqs_for_plan(&plan, rootfs.path(), &config)
|
||||
.expect_err("missing local manual source should fail before confirmation");
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("Manual source not found: missing.patch")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suppress_nested_install_output_for_planned_context() {
|
||||
let mut env = TestEnv::new();
|
||||
|
||||
+31
-17
@@ -62,11 +62,42 @@ fn manual_url_entries(manual: &crate::package::ManualSource) -> Vec<String> {
|
||||
/// Remote entries are fetched into the manual-source cache so build-time source
|
||||
/// preparation can reuse the verified result later. Git manual sources prime
|
||||
/// their mirror cache and validate revision reachability.
|
||||
pub fn preflight_local_manual_sources(spec: &PackageSpec) -> Result<()> {
|
||||
if spec.manual_sources.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for manual in &spec.manual_sources {
|
||||
let local_entries = manual_local_entries(manual);
|
||||
for raw_file in local_entries {
|
||||
let file = expand_manual_source_value(spec, &raw_file);
|
||||
let src_path = spec.spec_dir.join(&file);
|
||||
if !src_path.exists() {
|
||||
bail!(
|
||||
"Manual source not found: {} (expected at {})",
|
||||
file,
|
||||
src_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(expected_hash) = manual.sha256.as_ref().filter(|h| *h != "skip")
|
||||
&& !verify_file_hash(&src_path, expected_hash)?
|
||||
{
|
||||
bail!("Checksum mismatch for {}: expected {}", file, expected_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn preflight_manual_sources(spec: &PackageSpec, cache_dir: &Path) -> Result<()> {
|
||||
if spec.manual_sources.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
preflight_local_manual_sources(spec)?;
|
||||
|
||||
let manual_entry_count: usize = spec
|
||||
.manual_sources
|
||||
.iter()
|
||||
@@ -79,23 +110,6 @@ pub fn preflight_manual_sources(spec: &PackageSpec, cache_dir: &Path) -> Result<
|
||||
let url_entries = manual_url_entries(manual);
|
||||
|
||||
if !local_entries.is_empty() {
|
||||
for raw_file in local_entries {
|
||||
let file = expand_manual_source_value(spec, &raw_file);
|
||||
let src_path = spec.spec_dir.join(&file);
|
||||
if !src_path.exists() {
|
||||
bail!(
|
||||
"Manual source not found: {} (expected at {})",
|
||||
file,
|
||||
src_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(expected_hash) = manual.sha256.as_ref().filter(|h| *h != "skip")
|
||||
&& !verify_file_hash(&src_path, expected_hash)?
|
||||
{
|
||||
bail!("Checksum mismatch for {}: expected {}", file, expected_hash);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$#" -ne 15 ]; then
|
||||
echo "usage: $0 <cargo> <src_root> <build_root> <profile> <release_flag> <build_static> <autotools_pkg> <cmake_pkg> <meson_pkg> <perl_pkg> <custom_pkg> <python_pkg> <rust_pkg> <makefile_pkg> <output>" >&2
|
||||
if [ "$#" -ne 16 ]; then
|
||||
echo "usage: $0 <cargo> <src_root> <build_root> <profile> <release_flag> <build_static> <autotools_pkg> <cmake_pkg> <meson_pkg> <perl_pkg> <custom_pkg> <python_pkg> <rust_pkg> <makefile_pkg> <development_pkg> <output>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
@@ -20,7 +20,8 @@ custom_pkg="${11}"
|
||||
python_pkg="${12}"
|
||||
rust_pkg="${13}"
|
||||
makefile_pkg="${14}"
|
||||
output="${15}"
|
||||
development_pkg="${15}"
|
||||
output="${16}"
|
||||
|
||||
cargo_home="$build_root/cargo-home"
|
||||
cargo_target_dir="$build_root/cargo-target"
|
||||
@@ -38,6 +39,7 @@ export DEPOT_CUSTOM_PACKAGE="$custom_pkg"
|
||||
export DEPOT_PYTHON_PACKAGE="$python_pkg"
|
||||
export DEPOT_RUST_PACKAGE="$rust_pkg"
|
||||
export DEPOT_MAKEFILE_PACKAGE="$makefile_pkg"
|
||||
export DEPOT_DEVELOPMENT_PACKAGE="$development_pkg"
|
||||
|
||||
if [ "$release_flag" = "1" ]; then
|
||||
"$cargo_bin" build --locked --manifest-path "$src_root/Cargo.toml" --release
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$#" -ne 14 ]; then
|
||||
echo "usage: $0 <cargo> <src_root> <build_root> <profile> <release_flag> <build_static> <autotools_pkg> <cmake_pkg> <meson_pkg> <perl_pkg> <custom_pkg> <python_pkg> <rust_pkg> <makefile_pkg>" >&2
|
||||
if [ "$#" -ne 15 ]; then
|
||||
echo "usage: $0 <cargo> <src_root> <build_root> <profile> <release_flag> <build_static> <autotools_pkg> <cmake_pkg> <meson_pkg> <perl_pkg> <custom_pkg> <python_pkg> <rust_pkg> <makefile_pkg> <development_pkg>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
@@ -20,6 +20,7 @@ custom_pkg="${11}"
|
||||
python_pkg="${12}"
|
||||
rust_pkg="${13}"
|
||||
makefile_pkg="${14}"
|
||||
development_pkg="${15}"
|
||||
|
||||
cargo_home="$build_root/cargo-home"
|
||||
cargo_target_dir="$build_root/cargo-target"
|
||||
@@ -37,6 +38,7 @@ export DEPOT_CUSTOM_PACKAGE="$custom_pkg"
|
||||
export DEPOT_PYTHON_PACKAGE="$python_pkg"
|
||||
export DEPOT_RUST_PACKAGE="$rust_pkg"
|
||||
export DEPOT_MAKEFILE_PACKAGE="$makefile_pkg"
|
||||
export DEPOT_DEVELOPMENT_PACKAGE="$development_pkg"
|
||||
|
||||
if [ "$release_flag" = "1" ]; then
|
||||
exec "$cargo_bin" test --locked --manifest-path "$src_root/Cargo.toml" --profile "$profile"
|
||||
|
||||
Reference in New Issue
Block a user