Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Meson parity #373

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/example-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,20 @@ jobs:
sudo cp -r temp/usr/local/* /usr/local/

- name: Test pkg-config
if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
if: startsWith(matrix.os, 'macos')
run: |
set -x
test "$(pkg-config --cflags example_project)" = "-I/usr/local/include/example-project-0.1"
test "$(pkg-config --libs example_project)" = "-L/usr/local/lib -lexample-project"

- name: Test pkg-config
if: startsWith(matrix.os, 'ubuntu')
run: |
set -x
ARCHDIR=`dpkg-architecture -qDEB_HOST_MULTIARCH`
test "$(pkg-config --cflags example_project)" = "-I/usr/local/include/example-project-0.1"
test "$(pkg-config --libs example_project)" = "-L/usr/local/lib/${ARCHDIR} -lexample-project"

- name: Update dynamic linker cache
if: startsWith(matrix.os, 'ubuntu')
run: |
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ cargo = "0.79.0"
cargo-util = "0.2"
semver = "1.0.3"
log = "0.4"
clap = { version="4.0.29", features=["color", "derive", "cargo"] }
clap = { version = "4.0.29", features = ["color", "derive", "cargo", "string"] }
regex = "1.5.6"
cbindgen = { version="0.26.0", default-features=false }
toml = "0.8"
Expand Down
29 changes: 21 additions & 8 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ fn compile_with_exec(
let pkg = &unit.pkg;
let capi_config = load_manifest_capi_config(pkg, rustc_target)?;
let name = &capi_config.library.name;
let install_paths = InstallPaths::new(name, args, &capi_config);
let install_paths = InstallPaths::new(name, rustc_target, args, &capi_config);
let pkg_rustflags = &capi_config.library.rustflags;

let mut leaf_args: Vec<String> = rustc_target
Expand Down Expand Up @@ -1018,9 +1018,15 @@ impl CPackage {

let name = &capi_config.library.name;

let install_paths = InstallPaths::new(name, args, &capi_config);
let build_targets =
BuildTargets::new(name, rustc_target, root_output, libkinds, &capi_config)?;
let install_paths = InstallPaths::new(name, rustc_target, args, &capi_config);
let build_targets = BuildTargets::new(
name,
rustc_target,
root_output,
libkinds,
&capi_config,
args.get_flag("meson"),
)?;

let finger_print = FingerPrint::new(&id, root_output, &build_targets, &install_paths);

Expand All @@ -1043,15 +1049,15 @@ pub fn cbuild(
) -> anyhow::Result<(Vec<CPackage>, CompileOptions)> {
let rustc = config.load_global_rustc(Some(ws))?;
let targets = args.targets()?;
let target = match targets.len() {
0 => rustc.host.to_string(),
1 => targets[0].to_string(),
let (target, is_target_overridden) = match targets.len() {
0 => (rustc.host.to_string(), false),
1 => (targets[0].to_string(), true),
_ => {
anyhow::bail!("Multiple targets not supported yet");
}
};

let rustc_target = target::Target::new(&target)?;
let rustc_target = target::Target::new(Some(&target), is_target_overridden)?;

let default_kind = || match (rustc_target.os.as_str(), rustc_target.env.as_str()) {
("none", _) | (_, "musl") => vec!["staticlib"],
Expand Down Expand Up @@ -1209,6 +1215,7 @@ pub fn cbuild(
&root_output,
&libkinds,
capi_config,
args.get_flag("meson"),
)?;

if let (Some(from_static_lib), Some(to_static_lib)) = (
Expand All @@ -1223,6 +1230,12 @@ pub fn cbuild(
) {
copy(from_shared_lib, to_shared_lib)?;
}
if let (Some(from_debug_info), Some(to_debug_info)) = (
from_build_targets.debug_info.as_ref(),
build_targets.debug_info.as_ref(),
) {
copy(from_debug_info, to_debug_info)?;
}
}

cpkg.finger_print.static_libs = static_libs;
Expand Down
60 changes: 56 additions & 4 deletions src/build_targets.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ffi::OsString;
use std::path::{Path, PathBuf};

use crate::build::{CApiConfig, InstallTarget};
use crate::install::LibType;
use crate::target::Target;

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -43,14 +45,17 @@ fn extra_targets(

#[derive(Debug, Clone)]
pub struct BuildTargets {
pub name: String,
pub include: Option<PathBuf>,
pub static_lib: Option<PathBuf>,
pub shared_lib: Option<PathBuf>,
pub impl_lib: Option<PathBuf>,
pub debug_info: Option<PathBuf>,
pub def: Option<PathBuf>,
pub pc: PathBuf,
pub target: Target,
pub extra: ExtraTargets,
pub use_meson_naming_convention: bool,
}

impl BuildTargets {
Expand All @@ -60,6 +65,7 @@ impl BuildTargets {
targetdir: &Path,
libkinds: &[&str],
capi_config: &CApiConfig,
use_meson_naming_convention: bool,
) -> anyhow::Result<BuildTargets> {
let pc = targetdir.join(format!("{}.pc", &capi_config.pkg_config.filename));
let include = if capi_config.header.enabled {
Expand All @@ -75,7 +81,8 @@ impl BuildTargets {
let os = &target.os;
let env = &target.env;

let (shared_lib, static_lib, impl_lib, def) = match (os.as_str(), env.as_str()) {
let (shared_lib, static_lib, impl_lib, debug_info, def) = match (os.as_str(), env.as_str())
{
("none", _)
| ("linux", _)
| ("freebsd", _)
Expand All @@ -87,12 +94,12 @@ impl BuildTargets {
| ("emscripten", _) => {
let static_lib = targetdir.join(format!("lib{lib_name}.a"));
let shared_lib = targetdir.join(format!("lib{lib_name}.so"));
(shared_lib, static_lib, None, None)
(shared_lib, static_lib, None, None, None)
}
("macos", _) | ("ios", _) | ("tvos", _) => {
let static_lib = targetdir.join(format!("lib{lib_name}.a"));
let shared_lib = targetdir.join(format!("lib{lib_name}.dylib"));
(shared_lib, static_lib, None, None)
(shared_lib, static_lib, None, None, None)
}
("windows", env) => {
let static_lib = if env == "msvc" {
Expand All @@ -107,7 +114,12 @@ impl BuildTargets {
targetdir.join(format!("{lib_name}.dll.a"))
};
let def = targetdir.join(format!("{lib_name}.def"));
(shared_lib, static_lib, Some(impl_lib), Some(def))
let pdb = if env == "msvc" {
Some(targetdir.join(format!("{lib_name}.pdb")))
} else {
None
};
(shared_lib, static_lib, Some(impl_lib), pdb, Some(def))
}
_ => unimplemented!("The target {}-{} is not supported yet", os, env),
};
Expand All @@ -131,9 +143,49 @@ impl BuildTargets {
static_lib,
shared_lib,
impl_lib,
debug_info,
def,
use_meson_naming_convention,
name: name.into(),
target: target.clone(),
extra: Default::default(),
})
}

fn lib_type(&self) -> LibType {
LibType::from_build_targets(self)
}

pub fn debug_info_file_name(&self, bindir: &Path, libdir: &Path) -> Option<PathBuf> {
match self.lib_type() {
// FIXME: Requires setting split-debuginfo to packed and
// specifying the corresponding file name convention
// in BuildTargets::new.
LibType::So | LibType::Dylib => {
Some(libdir.join(self.debug_info.as_ref()?.file_name()?))
}
LibType::Windows => Some(bindir.join(self.debug_info.as_ref()?.file_name()?)),
}
}

pub fn static_output_file_name(&self) -> Option<OsString> {
match self.lib_type() {
LibType::Windows => {
if self.static_lib.is_some() && self.use_meson_naming_convention {
Some(format!("lib{}.a", self.name).into())
} else {
Some(self.static_lib.as_ref()?.file_name()?.to_owned())
}
}
_ => Some(self.static_lib.as_ref()?.file_name()?.to_owned()),
}
}

pub fn shared_output_file_name(&self) -> Option<OsString> {
if self.shared_lib.is_some() && self.use_meson_naming_convention {
Some(format!("lib{}.dll", self.name).into())
} else {
Some(self.shared_lib.as_ref()?.file_name().unwrap().to_owned())
}
}
}
29 changes: 26 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use cargo_util::{ProcessBuilder, ProcessError};

use clap::{Arg, ArgAction, ArgMatches, Command, CommandFactory, Parser};

use crate::target::Target;

// TODO: convert to a function using cargo opt()
#[allow(dead_code)]
#[derive(Clone, Debug, Parser)]
Expand All @@ -30,7 +32,7 @@ struct Common {
includedir: PathBuf,
/// Path to directory for installing generated executable files
#[clap(long = "bindir", default_value = "bin")]
bindir: PathBuf,
bindir: Option<PathBuf>,
/// Path to directory for installing generated pkg-config .pc files
///
/// [default: {libdir}/pkgconfig]
Expand All @@ -50,10 +52,14 @@ struct Common {
#[clap(long = "crt-static")]
/// Build the library embedding the C runtime
crt_static: bool,
/// Use the Linux/Meson library naming convention on Windows
#[clap(long = "meson-paths", default_value = "false")]
meson: bool,
}

fn base_cli() -> Command {
Common::command()
let default_target = Target::new::<&str>(None, false);
let app = Common::command()
.allow_external_subcommands(true)
.arg(flag("version", "Print version info and exit").short('V'))
.arg(flag("list", "List installed commands"))
Expand Down Expand Up @@ -104,7 +110,24 @@ fn base_cli() -> Command {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_build_plan()
.arg_build_plan();

if let Ok(t) = default_target {
app.mut_arg("prefix", |a| {
a.default_value(t.default_prefix().as_os_str().to_os_string())
})
.mut_arg("libdir", |a| {
a.default_value(t.default_libdir().as_os_str().to_os_string())
})
.mut_arg("datadir", |a| {
a.default_value(t.default_datadir().as_os_str().to_os_string())
})
.mut_arg("includedir", |a| {
a.default_value(t.default_includedir().as_os_str().to_os_string())
})
} else {
app
}
}

pub fn subcommand_build(name: &'static str, about: &'static str) -> Command {
Expand Down
64 changes: 50 additions & 14 deletions src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use cargo_util::paths::{copy, create_dir_all};

use crate::build::*;
use crate::build_targets::BuildTargets;
use crate::target::Target;

fn append_to_destdir(destdir: Option<&Path>, path: &Path) -> PathBuf {
if let Some(destdir) = destdir {
Expand Down Expand Up @@ -190,6 +191,7 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> {
install_path_lib.push(subdir);
}

let install_path_bin = append_to_destdir(destdir.as_deref(), &paths.bindir);
let install_path_lib = append_to_destdir(destdir.as_deref(), &install_path_lib);
let install_path_pc = append_to_destdir(destdir.as_deref(), &paths.pkgconfigdir);
let install_path_include = append_to_destdir(destdir.as_deref(), &paths.includedir);
Expand Down Expand Up @@ -225,10 +227,9 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> {

if let Some(ref static_lib) = build_targets.static_lib {
ws.gctx().shell().status("Installing", "static library")?;
copy(
static_lib,
install_path_lib.join(static_lib.file_name().unwrap()),
)?;
let file_name = build_targets.static_output_file_name().unwrap();

copy(static_lib, install_path_lib.join(file_name))?;
}

if let Some(ref shared_lib) = build_targets.shared_lib {
Expand All @@ -241,7 +242,7 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> {
lib.install(capi_config, shared_lib, &install_path_lib)?;
}
LibType::Windows => {
let lib_name = shared_lib.file_name().unwrap();
let lib_name = build_targets.shared_output_file_name().unwrap();

if capi_config.library.install_subdir.is_none() {
let install_path_bin = append_to_destdir(destdir.as_deref(), &paths.bindir);
Expand All @@ -254,7 +255,11 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> {
}
if capi_config.library.import_library {
let impl_lib = build_targets.impl_lib.as_ref().unwrap();
let impl_lib_name = impl_lib.file_name().unwrap();
let impl_lib_name = if build_targets.use_meson_naming_convention {
format!("{}.lib", build_targets.name).into()
} else {
impl_lib.file_name().unwrap().to_owned()
};
copy(impl_lib, install_path_lib.join(impl_lib_name))?;
let def = build_targets.def.as_ref().unwrap();
let def_name = def.file_name().unwrap();
Expand All @@ -263,6 +268,18 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> {
}
}
}

if let Some(ref debug_info) = build_targets.debug_info {
ws.gctx()
.shell()
.status("Installing", "debugging information")?;
let destination_path = build_targets
.debug_info_file_name(&install_path_bin, &install_path_lib)
.unwrap();

create_dir_all(destination_path.parent().unwrap())?;
copy(debug_info, destination_path)?;
}
}

Ok(())
Expand All @@ -280,16 +297,35 @@ pub struct InstallPaths {
pub pkgconfigdir: PathBuf,
}

fn get_path_or(args: &ArgMatches, id: &str, f: impl FnOnce() -> PathBuf) -> PathBuf {
if matches!(
args.value_source(id),
Some(clap::parser::ValueSource::DefaultValue)
) {
f()
} else {
args.get_one::<PathBuf>(id).unwrap().to_owned()
}
}

impl InstallPaths {
pub fn new(_name: &str, args: &ArgMatches, capi_config: &CApiConfig) -> Self {
pub fn new(
_name: &str,
rustc_target: &Target,
args: &ArgMatches,
capi_config: &CApiConfig,
) -> Self {
let destdir = args.get_one::<PathBuf>("destdir").map(PathBuf::from);
let prefix = args
.get_one::<PathBuf>("prefix")
.map(PathBuf::from)
.unwrap_or_else(|| "/usr/local".into());
let libdir = prefix.join(args.get_one::<PathBuf>("libdir").unwrap());
let includedir = prefix.join(args.get_one::<PathBuf>("includedir").unwrap());
let datarootdir = prefix.join(args.get_one::<PathBuf>("datarootdir").unwrap());
let prefix = get_path_or(args, "prefix", || rustc_target.default_prefix());
let libdir = prefix.join(get_path_or(args, "libdir", || {
rustc_target.default_libdir()
}));
let includedir = prefix.join(get_path_or(args, "includedir", || {
rustc_target.default_includedir()
}));
let datarootdir = prefix.join(get_path_or(args, "datarootdir", || {
rustc_target.default_datadir()
}));
let datadir = args
.get_one::<PathBuf>("datadir")
.map(|d| prefix.join(d))
Expand Down
Loading
Loading