Skip to content

Commit

Permalink
AOT package script (#1836)
Browse files Browse the repository at this point in the history
fixes #1771
commit-id:40843cae

---

**Stack**:
- #1846
- #1836⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do
not merge manually using the UI - doing so may have unexpected results.*

Co-authored-by: Mateusz Kowalski <[email protected]>
  • Loading branch information
maciektr and wawel37 authored Dec 16, 2024
1 parent 2c298c1 commit 40fda22
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 6 deletions.
22 changes: 22 additions & 0 deletions scarb/src/ops/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const RESERVED_FILES: &[&str] = &[
VCS_INFO_FILE_NAME,
];

const SCARB_PREPACKAGE_SCRIPT_NAME: &str = "package";

#[derive(Clone)]
pub struct PackageOpts {
pub allow_dirty: bool,
Expand Down Expand Up @@ -166,6 +168,8 @@ fn package_one_impl(
check_metadata(pkg, ws.config())?;
}

run_prepackage_script(pkg, ws)?;

let recipe = prepare_archive_recipe(pkg, opts, ws)?;
let num_files = recipe.len();

Expand Down Expand Up @@ -218,6 +222,24 @@ fn package_one_impl(
Ok(dst)
}

fn run_prepackage_script(package: &Package, ws: &Workspace<'_>) -> Result<()> {
if let Some(script_definition) = package.manifest.scripts.get(SCARB_PREPACKAGE_SCRIPT_NAME) {
// Ensure no two instance of Scarb will run `package` script at the same time.
let _guard = ws.target_dir().child("scarb").advisory_lock(
".scarb-package.lock",
"`package` script",
ws.config(),
);
ws.config().ui().print(Status::new(
"Running",
&format!("`package` script for `{}`", package.id.name),
));
ops::execute_script(script_definition, &[], ws, package.root(), None)
} else {
Ok(())
}
}

fn list_one_impl(
pkg_id: PackageId,
opts: &PackageOpts,
Expand Down
103 changes: 97 additions & 6 deletions scarb/tests/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use assert_fs::prelude::*;
use assert_fs::TempDir;
use indoc::{formatdoc, indoc};
use itertools::Itertools;
use libloading::library_filename;
use scarb::DEFAULT_TARGET_DIR_NAME;
use scarb_build_metadata::CAIRO_VERSION;
use scarb_test_support::cairo_plugin_project_builder::CairoPluginProjectBuilder;
Expand All @@ -22,7 +23,7 @@ use scarb_test_support::workspace_builder::WorkspaceBuilder;
use test_case::test_case;

struct PackageChecker {
actual_files: HashMap<PathBuf, String>,
actual_files: HashMap<PathBuf, Vec<u8>>,
base_name: PathBuf,
}

Expand All @@ -39,7 +40,7 @@ impl PackageChecker {
fn assert(path: &Path) -> Self {
let mut archive = Self::open(path);

let actual_files: HashMap<PathBuf, String> = archive
let actual_files: HashMap<PathBuf, Vec<u8>> = archive
.entries()
.expect("failed to get archive entries")
.map(|entry| {
Expand All @@ -48,10 +49,11 @@ impl PackageChecker {
.path()
.expect("failed to get archive entry path")
.into_owned();
let mut contents = String::new();
let mut contents: Vec<u8> = Vec::new();
entry
.read_to_string(&mut contents)
.read_to_end(&mut contents)
.expect("failed to read archive entry contents");

(name, contents)
})
.collect();
Expand Down Expand Up @@ -104,9 +106,11 @@ impl PackageChecker {

fn read_file(&self, path: impl AsRef<Path>) -> &str {
let path = self.base_name.join(path);
self.actual_files
let buf = self
.actual_files
.get(&path)
.unwrap_or_else(|| panic!("missing file in package tarball: {}", path.display()))
.unwrap_or_else(|| panic!("missing file in package tarball: {}", path.display()));
std::str::from_utf8(buf).expect("file is not valid UTF-8")
}

fn file_eq(&self, path: impl AsRef<Path>, expected_contents: &str) -> &Self {
Expand Down Expand Up @@ -1676,3 +1680,90 @@ fn files_that_dont_exist_during_packaging_cannot_be_included() {
2: [..]
"#});
}

#[test]
fn package_script_is_run() {
let t = TempDir::new().unwrap();
simple_project()
.manifest_package_extra(indoc! {r#"
[scripts]
package = "echo 'Hello!'"
"#})
.build(&t);
Scarb::quick_snapbox()
.current_dir(&t)
.arg("package")
.arg("--no-metadata")
.assert()
.success()
.stdout_matches(indoc! {r#"
[..]Packaging foo v1.0.0 ([..]Scarb.toml)
[..]Running `package` script for `foo`
Hello!
[..]Verifying foo-1.0.0.tar.zst
[..]Compiling foo v1.0.0 ([..]Scarb.toml)
[..]Finished `dev` profile target(s) in [..]
[..]Packaged [..] files[..]
"#});
}

#[test]
fn package_proc_macro_with_package_script() {
let t = TempDir::new().unwrap();
t.child("target/scarb/cairo-plugin")
.create_dir_all()
.unwrap();

let dll_filename = library_filename("foo");
let dll_filename = dll_filename.to_string_lossy().to_string();

let script_code = format!(
"cargo build --release && cp target/release/{dll_filename} target/scarb/cairo-plugin"
);

CairoPluginProjectBuilder::start()
.name("foo")
.scarb_project(|b| {
b.name("foo")
.version("1.0.0")
.manifest_package_extra(indoc! {r#"
include = ["target/scarb/cairo-plugin"]
"#})
.manifest_extra(formatdoc! {r#"
[cairo-plugin]
[scripts]
package = "{script_code}"
"#})
})
.lib_rs(indoc! {r#"
use cairo_lang_macro::{ProcMacroResult, TokenStream, attribute_macro};
#[attribute_macro]
pub fn some(_attr: TokenStream, token_stream: TokenStream) -> ProcMacroResult {
ProcMacroResult::new(token_stream)
}
"#})
.build(&t);

Scarb::quick_snapbox()
.current_dir(&t)
.arg("package")
.arg("--no-metadata")
.assert()
.success();

let expected_shared_lib_file = format!("target/scarb/cairo-plugin/{dll_filename}");

PackageChecker::assert(&t.child("target/package/foo-1.0.0.tar.zst"))
.name_and_version("foo", "1.0.0")
.contents(&[
"VERSION",
"Scarb.orig.toml",
"Scarb.toml",
&expected_shared_lib_file,
"Cargo.toml",
"Cargo.orig.toml",
"src/lib.rs",
]);
}
8 changes: 8 additions & 0 deletions website/docs/reference/scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ bar = "echo 'World!'"
This section should not contain any values with type different from string, including subtables, arrays, or numbers.
In case the section is empty, it will be ignored.

### Special script names

Some script names are reserved for special purposes and their execution might be associated with additional logic.
The following script names are reserved:

1. `test` - This script will be executed when you run `scarb test` command.
2. `package` - This script will be executed before the packaging process when you run `scarb package` command.

## Listing scripts

To list all available scripts, you can use `scarb run` command.
Expand Down
3 changes: 3 additions & 0 deletions website/docs/registries/publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ Use the `scarb package` command to create an archive of your package.
You can read about the package compression algorithm and contents in the [Package tarball](./package-tarball) section.
Basically when you run the command, Scarb gathers the source code of your package along with metadata files, such as the
manifest file, and places them in an archive in `target/package` directory.
You can define an optional script called `package` (learn more on [scripts here](../reference/scripts.md)) in your
manifest file, which will be executed directly before the packaging process.
This can be useful for instance to create some additional files to be included in the package.

If you are in a Git repository, Scarb will first check if the repo state is clean and error out in case of any changes
present in the Git working directory.
Expand Down

0 comments on commit 40fda22

Please sign in to comment.