Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ You can download a prebuilt binary from the [Releases](https://github.com/Jake-S
* `--manifest` - Specifies a top level manifest to use, rather than downloading it from Microsoft. This can be used to ensure the output is reproducible.
* `--sdk-version` - The specific SDK version to use. If not specified the latest SDK version in the manifest is used.
* `--crt-version` - The specific CRT version to use. If not specified the latest CRT version in the manifest is used.
* `--netfxsdk-version` - The specific .NET Framework SDK version to include. If not specified, the .NET Framework SDK is not included.
* `--timeout` - Specifies a timeout for long a single HTTP get request is allowed to take. The default is 60s.

### Env vars
Expand Down
37 changes: 36 additions & 1 deletion src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,32 @@ impl Ctx {
arches: u32,
variants: u32,
ops: crate::Ops,
) -> Result<(), Error> {
self.execute_with_netfxsdk(
packages,
payloads,
crt_version,
sdk_version,
None,
vcrd_version,
arches,
variants,
ops,
)
}

#[allow(clippy::too_many_arguments)]
pub fn execute_with_netfxsdk(
self: std::sync::Arc<Self>,
packages: std::collections::BTreeMap<String, crate::manifest::ManifestItem>,
payloads: Vec<WorkItem>,
crt_version: String,
sdk_version: String,
netfxsdk_version: Option<String>,
vcrd_version: Option<String>,
arches: u32,
variants: u32,
ops: crate::Ops,
) -> Result<(), Error> {
use rayon::prelude::*;

Expand All @@ -281,6 +307,7 @@ impl Ctx {
self.clone(),
&config.output,
config.use_winsysroot_style.then_some(&crt_version),
netfxsdk_version.as_deref(),
)?;
let mut config = config.clone();
config.output = splat_roots.root.clone();
Expand All @@ -292,6 +319,7 @@ impl Ctx {
self.clone(),
&config.splat_output,
config.use_winsysroot_style.then_some(&crt_version),
netfxsdk_version.as_deref(),
)?;

let config = crate::SplatConfig {
Expand Down Expand Up @@ -386,6 +414,7 @@ impl Ctx {
map.as_ref()
.filter(|_m| !matches!(ops, crate::Ops::Minimize(_))),
&sdk_version,
netfxsdk_version.clone(),
vcrd_version.clone(),
arches,
variants,
Expand Down Expand Up @@ -433,7 +462,13 @@ impl Ctx {
match ops {
crate::Ops::Minimize(config) => {
splat_links()?;
let results = crate::minimize::minimize(self, config, roots, &sdk_version)?;
let results = crate::minimize::minimize(
self,
config,
roots,
&sdk_version,
netfxsdk_version.as_deref(),
)?;

fn emit(name: &str, num: crate::minimize::FileNumbers) {
fn hb(bytes: u64) -> String {
Expand Down
182 changes: 180 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ pub enum PayloadKind {
AtlLibs,
CrtHeaders,
CrtLibs,
NetFxSdk,
SdkHeaders,
SdkLibs,
SdkStoreLibs,
Expand All @@ -190,10 +191,22 @@ pub enum PayloadKind {
pub struct PrunedPackageList {
pub crt_version: String,
pub sdk_version: String,
pub netfxsdk_version: Option<String>,
pub vcr_version: Option<String>,
pub payloads: Vec<Payload>,
}

#[derive(Clone, Debug)]
pub struct PruneConfig {
pub arches: u32,
pub variants: u32,
pub include_atl: bool,
pub include_debug_runtime: bool,
pub sdk_version: Option<String>,
pub crt_version: Option<String>,
pub netfxsdk_version: Option<String>,
}

/// Returns the list of packages that are actually needed for cross compilation
pub fn prune_pkg_list(
pkg_manifest: &manifest::PackageManifest,
Expand All @@ -204,6 +217,35 @@ pub fn prune_pkg_list(
sdk_version: Option<String>,
crt_version: Option<String>,
) -> Result<PrunedPackageList, Error> {
prune_pkg_list_with_config(
pkg_manifest,
PruneConfig {
arches,
variants,
include_atl,
include_debug_runtime,
sdk_version,
crt_version,
netfxsdk_version: None,
},
)
}

/// Returns the selected package list using the supplied pruning configuration.
pub fn prune_pkg_list_with_config(
pkg_manifest: &manifest::PackageManifest,
config: PruneConfig,
) -> Result<PrunedPackageList, Error> {
let PruneConfig {
arches,
variants,
include_atl,
include_debug_runtime,
sdk_version,
crt_version,
netfxsdk_version,
} = config;

// We only really need 2 core pieces from the manifest, the CRT (headers + libs)
// and the Windows SDK
let pkgs = &pkg_manifest.packages;
Expand All @@ -218,6 +260,12 @@ pub fn prune_pkg_list(
crt_version,
)?;
let sdk_version = get_sdk(pkgs, arches, sdk_version, &mut payloads)?;
let netfxsdk_version = if let Some(netfxsdk_version) = netfxsdk_version {
get_netfxsdk(pkgs, &netfxsdk_version, &mut payloads)?;
Some(netfxsdk_version)
} else {
None
};

let vcr_version = include_debug_runtime
.then(|| get_vcrd(pkgs, arches, &mut payloads).ok())
Expand All @@ -226,6 +274,7 @@ pub fn prune_pkg_list(
Ok(PrunedPackageList {
crt_version,
sdk_version,
netfxsdk_version,
vcr_version,
payloads,
})
Expand Down Expand Up @@ -565,6 +614,48 @@ fn get_atl(
Ok(())
}

fn get_netfxsdk(
pkgs: &BTreeMap<String, manifest::ManifestItem>,
version: &str,
pruned: &mut Vec<Payload>,
) -> Result<(), Error> {
let component_id = format!("Microsoft.Net.Component.{version}.SDK");
let component = pkgs
.get(&component_id)
.with_context(|| format!("unable to find .NET Framework SDK component '{component_id}'"))?;

let package_id = format!("Microsoft.Net.{version}.SDK");
component
.dependencies
.get(&package_id)
.with_context(|| format!("'{component_id}' does not depend on '{package_id}'"))?;

let sdk = pkgs
.get(&package_id)
.with_context(|| format!("unable to find .NET Framework SDK package '{package_id}'"))?;

let msi = sdk
.payloads
.iter()
.find(|payload| payload.file_name.ends_with(".msi"))
.with_context(|| format!("unable to find .NET Framework SDK MSI in '{}'", sdk.id))?;

pruned.push(Payload {
filename: format!("netfxsdk_{version}.msi").into(),
sha256: msi.sha256.clone(),
url: msi.url.clone(),
size: msi.size,
install_size: (sdk.payloads.len() == 1)
.then_some(sdk)
.and_then(|mi| mi.install_sizes.as_ref().and_then(|is| is.target_drive)),
kind: PayloadKind::NetFxSdk,
target_arch: None,
variant: None,
});

Ok(())
}

fn get_latest_sdk_version<'keys>(
keys: impl Iterator<Item = &'keys String>,
) -> Option<(String, versions::Version)> {
Expand Down Expand Up @@ -856,6 +947,7 @@ use serde::{Deserialize, Serialize, de};
#[derive(Default)]
pub struct Map {
pub crt: Block,
pub netfxsdk: Block,
pub sdk: Block,
pub vcrd: Block,
}
Expand All @@ -879,22 +971,28 @@ impl<'de> Deserialize<'de> for Map {
A: de::MapAccess<'de>,
{
let mut crt = None;
let mut netfxsdk = None;
let mut sdk = None;
let mut vcrd = None;

while let Some((k, v)) = map.next_entry()? {
match k {
"crt" => crt = Some(v),
"netfxsdk" => netfxsdk = Some(v),
"sdk" => sdk = Some(v),
"vcrd" => vcrd = Some(v),
unknown => {
return Err(de::Error::unknown_field(unknown, &["crt", "sdk", "vcrd"]));
return Err(de::Error::unknown_field(
unknown,
&["crt", "netfxsdk", "sdk", "vcrd"],
));
}
}
}

Ok(Map {
crt: crt.ok_or_else(|| de::Error::missing_field("crt"))?,
netfxsdk: netfxsdk.unwrap_or_default(),
sdk: sdk.ok_or_else(|| de::Error::missing_field("sdk"))?,
vcrd: vcrd.ok_or_else(|| de::Error::missing_field("vcrd"))?,
})
Expand All @@ -915,6 +1013,7 @@ impl Serialize for Map {
let mut s = serializer.serialize_map(None)?;

s.serialize_entry("crt", &self.crt)?;
s.serialize_entry("netfxsdk", &self.netfxsdk)?;
s.serialize_entry("sdk", &self.sdk)?;
s.serialize_entry("vcrd", &self.vcrd)?;

Expand All @@ -925,6 +1024,7 @@ impl Serialize for Map {
impl Map {
fn clear(&mut self) {
self.crt.clear();
self.netfxsdk.clear();
self.sdk.clear();
self.vcrd.clear();
}
Expand Down Expand Up @@ -1005,6 +1105,8 @@ impl Block {
pub enum SectionKind {
CrtHeader,
CrtLib,
NetFxSdkHeader,
NetFxSdkLib,
SdkHeader,
SdkLib,
VcrDebug,
Expand Down Expand Up @@ -1117,7 +1219,9 @@ fn symlink_on_windows_too(original: &str, link: &Path) -> Result<(), Error> {

#[cfg(test)]
mod test {
use super::get_latest_sdk_version as glsv;
use super::{Map, PayloadKind, get_latest_sdk_version as glsv, get_netfxsdk};
use crate::{manifest, util::Sha256};
use std::collections::BTreeMap;

#[test]
fn sdk_versions() {
Expand All @@ -1144,4 +1248,78 @@ mod test {
glsv(just_11.iter().chain(just_10.iter())).unwrap().0
);
}

#[test]
fn netfxsdk_selects_component_dependency() {
let package_id = "Microsoft.Net.4.8.1.SDK";
let mut dependencies = BTreeMap::new();
dependencies.insert(package_id.to_owned(), serde_json::Value::Null);

let component_id = "Microsoft.Net.Component.4.8.1.SDK";
let component = manifest::ManifestItem {
id: component_id.to_owned(),
version: "4.8.1".to_owned(),
kind: manifest::ItemKind::Component,
chip: None,
payloads: Vec::new(),
dependencies,
install_sizes: None,
};
let sdk = manifest::ManifestItem {
id: package_id.to_owned(),
version: "4.8.1".to_owned(),
kind: manifest::ItemKind::Msi,
chip: None,
payloads: vec![manifest::Payload {
file_name: "netfxsdk.msi".to_owned(),
sha256: Sha256([0x42; 32]),
size: 1234,
url: "https://example.invalid/netfxsdk.msi".to_owned(),
}],
dependencies: BTreeMap::new(),
install_sizes: Some(manifest::InstallSizes {
target_drive: Some(5678),
}),
};

let packages = BTreeMap::from([
(component_id.to_owned(), component),
(package_id.to_owned(), sdk),
]);
let mut selected = Vec::new();
get_netfxsdk(&packages, "4.8.1", &mut selected).unwrap();

assert_eq!(selected.len(), 1);
let payload = &selected[0];
assert_eq!(payload.filename, crate::Path::new("netfxsdk_4.8.1.msi"));
assert_eq!(payload.kind, PayloadKind::NetFxSdk);
assert_eq!(payload.size, 1234);
assert_eq!(payload.install_size, Some(5678));
}

#[test]
fn map_without_netfxsdk_remains_compatible() {
let legacy = r#"
[crt.headers]
[crt.libs]
[sdk.headers]
[sdk.libs]
[vcrd.headers]
[vcrd.libs]
"#;

let mut map: Map = toml::from_str(legacy).unwrap();
assert!(map.netfxsdk.headers.filter.is_empty());
assert!(map.netfxsdk.libs.filter.is_empty());

map.netfxsdk
.headers
.filter
.insert("um/metahost.h".to_owned());
let serialized = toml::to_string_pretty(&map).unwrap();
assert!(serialized.contains("[netfxsdk.headers]"));

let roundtrip: Map = toml::from_str(&serialized).unwrap();
assert!(roundtrip.netfxsdk.headers.filter.contains("um/metahost.h"));
}
}
Loading