Skip to content

Commit

Permalink
feat: add MOZJS_FROM_SOURCE (#450)
Browse files Browse the repository at this point in the history
* Build from source if archive fails

* Add MOZJS_FROM_SOURCE

* Update env variables behavior

- If MOZJS_CREATE_ARCHIVE is enabled, it will also build from source.
- If MOZJS_ARCHIVE is enabled, it will panic on extraction fail
  directly.

* Update release job to auto download if release-tag exists

* Update env name in release.yml

* Update prebuilt linking condition

* Add more cargo messages during archive linking

* Update info to normal prints

* Cargo fmt

* Update README.md

* Update mozjs-sys build script

* Update typos and style changes
  • Loading branch information
wusyong committed Mar 28, 2024
1 parent 0172cf4 commit 8603cbf
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 25 deletions.
13 changes: 8 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,22 @@ jobs:
runs-on: ${{ matrix.platform.os }}
env:
RELEASE_TAG: ${{ github.event_name == 'release' && github.ref_name || inputs.release-tag }}
MOZJS_ARCHIVE: libmozjs-${{ matrix.platform.target }}.tar.gz
steps:
- uses: actions/checkout@v4
- name: Download prebuilt mozjs from artifact
if: ${{ env.RELEASE_TAG == '' }}
uses: actions/download-artifact@v4
with:
name: libmozjs-${{ matrix.platform.target }}.tar.gz
- name: Download prebuilt mozjs from release
if: ${{ env.RELEASE_TAG != '' }}
- name: Build from archive
if: ${{ env.RELEASE_TAG == '' }}
env:
MOZJS_ARCHIVE: libmozjs-${{ matrix.platform.target }}.tar.gz
run: |
curl -L https://github.com/${{ github.repository_owner }}/mozjs/releases/download/${{ env.RELEASE_TAG }}/libmozjs-${{ matrix.platform.target }}.tar.gz -o libmozjs-${{ matrix.platform.target }}.tar.gz
- name: Build
cargo build --verbose --features streams
cargo test --tests --examples --verbose --features streams
- name: Build from auto-download
if: ${{ env.RELEASE_TAG != '' }}
run: |
cargo build --verbose --features streams
cargo test --tests --examples --verbose --features streams
34 changes: 24 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,36 @@ that are battle-tested in [Servo](https://servo.org/), split in two crates:
Mozjs is currently tracking SpiderMonkey on [ESR-115](https://searchfox.org/mozilla-esr115/source/) branch
(currently version 115.3).

## Building
## Building from Pre-built Archive

SpiderMonkey is very large and can take a long time to compile. If building with default
features, `mozjs` provides a pre-built archive which can be linked against. You can also create
your own archive and link to it. `mozjs` currently offers two environment variables to enable
this feature:

- `MOZJS_CREATE_ARCHIVE=1` will create a SpiderMonkey binary archive for release usage. It will
be created in the `target` directory.
- `MOZJS_ARCHIVE` can be used to build against a pre-built archive. Using this flag, compiling
SpiderMonkey and the bindgen wrappers is unnecessary. There are two ways to use it:
- `MOZJS_ARCHIVE=path/to/libmozjs.tar.gz`: This option will look for the archive at the local
path, extract it, and then link against the static libraries included in the archive.
- `MOZJS_ARCHIVE=https://url/to/release/page`: This option will download the archive from
the provided base URL, extract it, and then link against the static libraries included in the
archive. The base URL should be similar to `https://github.com/servo/mozjs/releases/`.
The build script will append the version and target accordingly. See the files at the example
URL for more details.

## Building from Source

If `MOZJS_FROM_SOURCE=1` or `MOZJS_CREATE_ARCHIVE` are enabled or linking against a
pre-built archive fails, `mozjs` will build SpiderMonkey from source.

### Linux

Install Python, Clang and `build-essential`, for example on a Debian-based Linux:

```sh
sudo apt-get install build-essential python3 python3-distutils llvm libclang-dev clang
sudo apt-get install build-essential python3 python3-distutils llvm libclang-dev clang curl
```

If you have more than one version of Clang installed, you can set the `LIBCLANG_PATH`
Expand Down Expand Up @@ -84,14 +106,6 @@ cargo build --features debugmozjs
cargo test --features debugmozjs
```

### Create and link prebuilt binary

Spidermonkey is very large which could take a long time to compile. If you are looking for prebuilt version of mozjs, you
can ask someone to build it and share to you to link it. Mozjs currently offer two environment variables to enable such work:

- `MOZJS_CREATE_ARCHIVE=1` can create a spidermonkey tarball for release usage. It will be created in `target` directory.
- `MOZJS_ARCHIVE=path/to/libmozjs.tar.gz` can use this tarball to extract and link the static libraries without compiling spidermonkey and bindgen wrappers.

### Usage for downstream consumers

Mozjs is currently not published to crates.io, but it can be used from git (binaries should use lockfile instead of `rev`):
Expand Down
81 changes: 71 additions & 10 deletions mozjs-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,16 @@ fn main() {
// Used by mozjs downstream, don't remove.
println!("cargo:outdir={}", build_dir.display());

if let Some(archive) = env::var_os("MOZJS_ARCHIVE") {
download_static_lib_binaries(&PathBuf::from(archive), &build_dir);
} else {
// Check if we can link with pre-built archive , and decide if it needs to build from source.
let mut build_from_source = should_build_from_source();
if !build_from_source {
if let Err(e) = link_static_lib_binaries(&build_dir) {
println!("cargo:warning=Failed to link pre-built archive by {e}. Building from source instead.");
build_from_source = true;
}
}

if build_from_source {
fs::create_dir_all(&build_dir).expect("could not create build dir");
build_spidermonkey(&build_dir);
build_jsapi(&build_dir);
Expand Down Expand Up @@ -91,6 +98,28 @@ fn main() {
}
}

/// Check env variable conditions to decide if we need to link pre-built archive first.
/// And then return bool value to notify if we need to build from source instead.
fn should_build_from_source() -> bool {
if env::var_os("MOZJS_FROM_SOURCE").is_some() {
println!("Environment variable MOZJS_FROM_SOURCE is set. Building from source directly.");
true
} else if env::var_os("MOZJS_CREATE_ARCHIVE").is_some() {
println!(
"Environment variable MOZJS_CREATE_ARCHIVE is set. Building from source directly."
);
true
} else if env::var_os("CARGO_FEATURE_DEBUGMOZJS").is_some() {
println!("debug-mozjs feature is enabled. Building from source directly.");
true
} else if !env::var_os("CARGO_FEATURE_STREAMS").is_some() {
println!("streams feature isn't enabled. Building from source directly.");
true
} else {
false
}
}

#[cfg(not(windows))]
fn find_make() -> OsString {
if let Some(make) = env::var_os("MAKE") {
Expand Down Expand Up @@ -787,22 +816,53 @@ fn decompress_static_lib(archive: &Path, build_dir: &Path) -> Result<(), std::io
let tar_gz = File::open(archive).unwrap_or({
let mut workspace_dir = get_cargo_target_dir(build_dir).unwrap().to_path_buf();
workspace_dir.pop();
File::open(workspace_dir.join(archive)).unwrap()
File::open(workspace_dir.join(archive))?
});
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(build_dir)?;
Ok(())
}

/// Download static library tarball instead of building it from source.
fn download_static_lib_binaries(archive: &Path, build_dir: &Path) {
// Only download the files if build directory doesn't exist.
if !build_dir.exists() {
// TODO download from https
decompress_static_lib(archive, build_dir).expect("Failed to decompress static libs");
/// Download the SpiderMonkey archive with cURL using the provided base URL. If it's None,
/// it will use `servo/mozjs`'s release page as the base URL.
fn download_archive(base: Option<&str>) -> Result<PathBuf, std::io::Error> {
let base = base.unwrap_or("https://github.com/servo/mozjs/releases/download");
let version = env::var("CARGO_PKG_VERSION").unwrap();
let target = env::var("TARGET").unwrap();
let archive_path = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("libmozjs.tar.gz");
if !archive_path.exists() {
if !Command::new("curl")
.arg("-L")
.arg("-f")
.arg("-s")
.arg("-o")
.arg(&archive_path)
.arg(format!(
"{base}/mozjs-sys-v{version}/libmozjs-{target}.tar.gz"
))
.status()?
.success()
{
return Err(std::io::Error::from(std::io::ErrorKind::NotFound));
}
}

Ok(archive_path)
}

/// Link against the static library archive instead of building SpiderMonkey from source.
fn link_static_lib_binaries(build_dir: &Path) -> Result<(), std::io::Error> {
if let Ok(archive) = env::var("MOZJS_ARCHIVE") {
// If the archive variable is present, assume it's a URL base to download from.
let archive = download_archive(Some(&archive)).unwrap_or(PathBuf::from(archive));
// Panic directly since the archive is specified manually.
decompress_static_lib(&archive, build_dir).unwrap();
} else {
let archive = download_archive(None)?;
decompress_static_lib(&archive, build_dir)?;
};

// Link static lib binaries
let target = env::var("TARGET").unwrap();
println!(
Expand All @@ -827,4 +887,5 @@ fn download_static_lib_binaries(archive: &Path, build_dir: &Path) {
println!("cargo:rustc-link-search=native={}", build_dir.display());
println!("cargo:rustc-link-lib=static=jsapi");
println!("cargo:rustc-link-lib=static=jsglue");
Ok(())
}

0 comments on commit 8603cbf

Please sign in to comment.