simics_package/artifacts/
mod.rs

1// Copyright (C) 2024 Intel Corporation
2// SPDX-License-Identifier: Apache-2.0
3
4//! Artifacts for a package. Typically, these artifacts are the main library for the package and
5//! any interfaces which are built as separate libraries by the interface declaration.
6
7use crate::{Error, Result, HOST_DIRNAME};
8use artifact_dependency::ARTIFACT_NAMEPARTS;
9use cargo_subcommand::Subcommand;
10use std::{fs::read_dir, path::PathBuf};
11use typed_builder::TypedBuilder;
12
13#[derive(TypedBuilder, Debug, Clone, Default)]
14/// A set of artifacts that will be added into a Simics package
15pub struct PackageArtifacts {
16    /// Source paths of signed libraries in the build directory. These will be copied into
17    /// $(HOST)/lib/
18    pub libs: Vec<PathBuf>,
19    /// Files mapping of in-package to on-disk files which will be used to generate the
20    /// package spec
21    pub files: Vec<(String, String)>,
22}
23
24impl PackageArtifacts {
25    /// Create a new `PackageArtifacts` from a `Subcommand` by reading the crate state and
26    /// obtaining build results including macro-built interfaces.
27    pub fn from_subcommand(subcommand: &Subcommand) -> Result<Self> {
28        let module_cdylib = subcommand
29            .artifacts()
30            .map(|a| {
31                subcommand.build_dir(subcommand.target()).join(format!(
32                    "{}{}{}",
33                    ARTIFACT_NAMEPARTS.0,
34                    a.name.replace('_', "-"),
35                    ARTIFACT_NAMEPARTS.1
36                ))
37            })
38            .find(|p| p.exists())
39            .ok_or_else(|| Error::CdylibArtifactNotFound {
40                package: subcommand.package().to_string(),
41            })?;
42
43        let module_artifact = PathBuf::from({
44            let file_name = module_cdylib
45                .file_name()
46                .ok_or_else(|| Error::FilenameNotFound {
47                    path: module_cdylib.to_path_buf(),
48                })?
49                .to_str()
50                .ok_or_else(|| Error::FilenameNotFound {
51                    path: module_cdylib.to_path_buf(),
52                })?;
53            let module_cdylib_dir =
54                module_cdylib
55                    .parent()
56                    .ok_or_else(|| Error::ParentNotFound {
57                        path: module_cdylib.to_path_buf(),
58                    })?;
59
60            module_cdylib_dir
61                .join(file_name.replace('_', "-"))
62                .to_str()
63                .ok_or_else(|| Error::FilenameNotFound {
64                    path: module_cdylib.to_path_buf(),
65                })?
66        });
67
68        let target_profile_build_dir = subcommand.build_dir(subcommand.target()).join("build");
69
70        // Find interfaces
71        let target_profile_build_subdirs = read_dir(target_profile_build_dir)?
72            .filter_map(|rd| rd.ok())
73            .map(|de| de.path())
74            .filter(|p| {
75                p.is_dir()
76                    && p.file_name().is_some_and(|n| {
77                        n.to_str()
78                            .is_some_and(|ns| ns.starts_with(subcommand.package()))
79                    })
80                    && !p
81                        .join(format!("build-script-build{}", ARTIFACT_NAMEPARTS.4))
82                        .exists()
83                    && p.join("out").is_dir()
84            })
85            .collect::<Vec<_>>();
86
87        // Source, Destination of interface libraries
88        let cdylib_out_artifacts = target_profile_build_subdirs
89            .iter()
90            .map(|bd| bd.join("out"))
91            .map(|od| {
92                read_dir(od).map(|rd| {
93                    Ok(rd
94                        .filter_map(|rd| rd.ok())
95                        .map(|de| de.path())
96                        .filter(|p| {
97                            p.file_name().is_some_and(|n| {
98                                n.to_str().is_some_and(|ns| {
99                                    ns.starts_with(ARTIFACT_NAMEPARTS.0)
100                                        && ns.ends_with(ARTIFACT_NAMEPARTS.1)
101                                })
102                            })
103                        })
104                        .collect::<Vec<_>>())
105                })?
106            })
107            .collect::<Result<Vec<_>>>()?
108            .into_iter()
109            .flatten()
110            .map(|a| {
111                a.file_name()
112                    .and_then(|n| n.to_str())
113                    .ok_or_else(|| Error::FilenameNotFound { path: a.clone() })
114                    .map(|t| (a.clone(), subcommand.build_dir(subcommand.target()).join(t)))
115            })
116            .collect::<Result<Vec<_>>>()?;
117
118        let cdylib_out_artifacts = cdylib_out_artifacts
119            .iter()
120            .map(|a| a.1.clone())
121            .collect::<Vec<_>>();
122
123        let libs = vec![module_artifact]
124            .into_iter()
125            .chain(cdylib_out_artifacts)
126            .collect::<Vec<_>>();
127
128        let host_dir = PathBuf::from(HOST_DIRNAME);
129        let lib_dir = host_dir.join("lib");
130
131        // Build the mapping from in-package relative path (which will be prefixed with the
132        // package directory name) to the on-disk path the file currently resides at. This is
133        // used later to generate the package tarball by appending all of these files at their
134        // correct locations
135        let files = libs
136            .iter()
137            .map(|file_path| {
138                file_path
139                    .canonicalize()
140                    .map_err(Error::from)
141                    .and_then(|file_path| {
142                        file_path
143                            .file_name()
144                            .ok_or_else(|| Error::FilenameNotFound {
145                                path: file_path.clone(),
146                            })
147                            .and_then(|file_name| {
148                                lib_dir
149                                    .join(file_name)
150                                    .to_str()
151                                    .ok_or_else(|| Error::PathConversionError {
152                                        path: lib_dir.join(file_name),
153                                    })
154                                    .and_then(|packaged_file_path| {
155                                        Ok((
156                                            packaged_file_path.to_string(),
157                                            file_path
158                                                .to_str()
159                                                .ok_or_else(|| Error::PathConversionError {
160                                                    path: file_path.clone(),
161                                                })?
162                                                .to_string(),
163                                        ))
164                                    })
165                            })
166                    })
167            })
168            .collect::<Result<Vec<_>>>()?;
169
170        Ok(Self { libs, files })
171    }
172}