Add option to use LTO in release builds

Shrinks rslib.so from about 40MB to about 26MB, at the cost of considerably
higher build time in a release build.
This commit is contained in:
Damien Elmes 2023-07-02 16:31:33 +10:00
parent 56a1046ff8
commit c6f429ab17
9 changed files with 71 additions and 22 deletions

View File

@ -4,7 +4,7 @@ set -e
export PATH="$PATH:/state/rust/cargo/bin"
export BUILD_ROOT=/state/build
export RELEASE=1
export RELEASE=2
ln -sf out/node_modules .
if [ $(uname -m) = "aarch64" ]; then

View File

@ -152,3 +152,7 @@ debug = 0
debug = 0
[profile.dev.package.rsbridge]
debug = 0
[profile.release-lto]
inherits = "release"
lto = true

View File

@ -8,6 +8,7 @@ use ninja_gen::archives::empty_manifest;
use ninja_gen::archives::with_exe;
use ninja_gen::archives::OnlineArchive;
use ninja_gen::archives::Platform;
use ninja_gen::build::BuildProfile;
use ninja_gen::cargo::CargoBuild;
use ninja_gen::cargo::RustOutput;
use ninja_gen::git::SyncSubmodule;
@ -269,7 +270,7 @@ fn build_pyoxidizer(build: &mut Build) -> Result<()> {
"--manifest-path={} --target-dir={} -p pyoxidizer",
"qt/bundle/PyOxidizer/Cargo.toml", "$builddir/bundle/rust"
),
release_override: Some(true),
release_override: Some(BuildProfile::Release),
},
)?;
Ok(())
@ -320,7 +321,8 @@ impl BuildAction for BuildBundle {
overriden_rust_target_triple()
.unwrap_or_else(|| Platform::current().as_rust_triple()),
),
true,
// our pyoxidizer bin uses lto on the release profile
BuildProfile::Release,
)],
);
}

View File

@ -3,6 +3,7 @@
use anyhow::Result;
use ninja_gen::action::BuildAction;
use ninja_gen::build::BuildProfile;
use ninja_gen::build::FilesHandle;
use ninja_gen::cargo::CargoBuild;
use ninja_gen::cargo::CargoClippy;
@ -202,7 +203,7 @@ pub fn check_minilints(build: &mut Build) -> Result<()> {
outputs: &[RustOutput::Binary("minilints")],
target: None,
extra_args: "-p minilints",
release_override: Some(false),
release_override: Some(BuildProfile::Debug),
},
)
}

View File

@ -19,7 +19,7 @@ use crate::input::BuildInput;
pub struct Build {
pub variables: HashMap<&'static str, String>,
pub buildroot: Utf8PathBuf,
pub release: bool,
pub build_profile: BuildProfile,
pub pools: Vec<(&'static str, usize)>,
pub trailing_text: String,
pub host_platform: Platform,
@ -40,7 +40,7 @@ impl Build {
let mut build = Build {
buildroot,
release: std::env::var("RELEASE").is_ok(),
build_profile: BuildProfile::from_env(),
host_platform: Platform::current(),
variables: Default::default(),
pools: Default::default(),
@ -102,7 +102,7 @@ impl Build {
};
let mut statement =
BuildStatement::from_build_action(group, action, &self.groups, self.release);
BuildStatement::from_build_action(group, action, &self.groups, self.build_profile);
if first_invocation {
let command = statement.prepare_command(command)?;
@ -218,7 +218,7 @@ struct BuildStatement<'a> {
env_vars: Vec<String>,
working_dir: Option<String>,
create_dirs: Vec<String>,
release: bool,
build_profile: BuildProfile,
bypass_runner: bool,
}
@ -227,7 +227,7 @@ impl BuildStatement<'_> {
group: &str,
mut action: impl BuildAction,
existing_outputs: &'a HashMap<String, Vec<String>>,
release: bool,
build_profile: BuildProfile,
) -> BuildStatement<'a> {
let mut stmt = BuildStatement {
existing_outputs,
@ -244,7 +244,7 @@ impl BuildStatement<'_> {
env_vars: Default::default(),
working_dir: None,
create_dirs: Default::default(),
release,
build_profile,
bypass_runner: action.bypass_runner(),
};
action.files(&mut stmt);
@ -328,6 +328,23 @@ fn expand_inputs(
vec
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum BuildProfile {
Debug,
Release,
ReleaseWithLto,
}
impl BuildProfile {
fn from_env() -> Self {
match std::env::var("RELEASE").unwrap_or_default().as_str() {
"1" => Self::Release,
"2" => Self::ReleaseWithLto,
_ => Self::Debug,
}
}
}
pub trait FilesHandle {
/// Add inputs to the build statement. Can be called multiple times with
/// different variables. This is a shortcut for calling .expand_inputs()
@ -391,7 +408,7 @@ pub trait FilesHandle {
/// at the folder.
fn create_dir_all(&mut self, key: &str, path: impl Into<String>);
fn release_build(&self) -> bool;
fn build_profile(&self) -> BuildProfile;
}
impl FilesHandle for BuildStatement<'_> {
@ -474,8 +491,8 @@ impl FilesHandle for BuildStatement<'_> {
expand_inputs(inputs, self.existing_outputs)
}
fn release_build(&self) -> bool {
self.release
fn build_profile(&self) -> BuildProfile {
self.build_profile
}
fn add_output_stamp(&mut self, path: impl Into<String>) {

View File

@ -7,6 +7,7 @@ use camino::Utf8PathBuf;
use crate::action::BuildAction;
use crate::archives::with_exe;
use crate::build::BuildProfile;
use crate::build::FilesHandle;
use crate::input::BuildInput;
use crate::inputs;
@ -31,7 +32,12 @@ impl RustOutput<'_> {
}
}
pub fn path(&self, rust_base: &Utf8Path, target: Option<&str>, release: bool) -> String {
pub fn path(
&self,
rust_base: &Utf8Path,
target: Option<&str>,
build_profile: BuildProfile,
) -> String {
let filename = match *self {
RustOutput::Binary(package) => {
if cfg!(windows) {
@ -56,20 +62,26 @@ impl RustOutput<'_> {
if let Some(target) = target {
path = path.join(target);
}
path = path
.join(if release { "release" } else { "debug" })
.join(filename);
path = path.join(profile_output_dir(build_profile)).join(filename);
path.to_string()
}
}
fn profile_output_dir(profile: BuildProfile) -> &'static str {
match profile {
BuildProfile::Debug => "debug",
BuildProfile::Release => "release",
BuildProfile::ReleaseWithLto => "release-lto",
}
}
#[derive(Debug, Default)]
pub struct CargoBuild<'a> {
pub inputs: BuildInput,
pub outputs: &'a [RustOutput<'a>],
pub target: Option<&'static str>,
pub extra_args: &'a str,
pub release_override: Option<bool>,
pub release_override: Option<BuildProfile>,
}
impl BuildAction for CargoBuild<'_> {
@ -80,8 +92,8 @@ impl BuildAction for CargoBuild<'_> {
fn files(&mut self, build: &mut impl FilesHandle) {
let release_build = self
.release_override
.unwrap_or_else(|| build.release_build());
let release_arg = if release_build { "--release" } else { "" };
.unwrap_or_else(|| build.build_profile());
let release_arg = profile_arg_for_cargo(release_build).unwrap_or_default();
let target_arg = if let Some(target) = self.target {
format!("--target {target}")
} else {
@ -114,6 +126,14 @@ impl BuildAction for CargoBuild<'_> {
}
}
fn profile_arg_for_cargo(profile: BuildProfile) -> Option<&'static str> {
match profile {
BuildProfile::Debug => None,
BuildProfile::Release => Some("--release"),
BuildProfile::ReleaseWithLto => Some("--profile release-lto"),
}
}
fn setup_flags(build: &mut Build) -> Result<()> {
build.once_only("cargo_flags_and_pool", |build| {
build.variable("cargo_flags", "--locked");

View File

@ -4,6 +4,7 @@
use anyhow::Result;
use crate::action::BuildAction;
use crate::build::BuildProfile;
use crate::build::FilesHandle;
use crate::cargo::CargoBuild;
use crate::cargo::RustOutput;
@ -33,7 +34,7 @@ impl BuildAction for ConfigureBuild {
outputs: &[RustOutput::Binary("configure")],
target: None,
extra_args: "-p configure",
release_override: Some(false),
release_override: Some(BuildProfile::Debug),
},
)?;
Ok(())

View File

@ -105,7 +105,8 @@ To run Anki in optimized mode, use:
./tools/runopt
```
Or set RELEASE=1.
Or set RELEASE=1 or RELEASE=2. The latter will further optimize the output, but make
the build much slower.
## Building redistributable wheels

View File

@ -55,3 +55,6 @@ build-mode-pyoxidizer-exe = []
# to the directory containing build artifacts produced by `pyoxidizer`. If not
# set, OUT_DIR will be used.
build-mode-prebuilt-artifacts = []
[profile.release]
lto = true