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
4 changes: 4 additions & 0 deletions docs/bundler/executables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ To build for macOS x64:

The segments of the `--target` value can appear in any order, as long as they're delimited by `-`.

Linux targets also accept a libc segment such as `-glibc` or `-musl` (for example `bun-linux-x64-glibc`), which selects that libc's build of Bun no matter which build is running. Without one, a Linux target uses the libc of the `bun` running the build: the table below describes a glibc build of Bun, while a musl build of Bun turns `bun-linux-x64` into a musl executable.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

| --target | Operating System | Architecture | Modern | Baseline | Libc |
| -------------------- | ---------------- | ------------ | ------ | -------- | ----- |
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
Expand Down Expand Up @@ -1282,6 +1284,8 @@ type CompileTarget =
| "bun-linux-x64-baseline"
| "bun-linux-x64-modern"
| "bun-linux-arm64"
| "bun-linux-x64-glibc"
| "bun-linux-arm64-glibc"
| "bun-linux-x64-musl"
| "bun-linux-arm64-musl"
| "bun-windows-x64"
Expand Down
141 changes: 67 additions & 74 deletions src/options_types/compile_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Default for CompileTarget {
}

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::IntoStaticStr)]
pub enum Libc {
/// The default libc for the target
/// "glibc" for linux, unspecified for other OSes
Expand Down Expand Up @@ -77,6 +77,15 @@ impl fmt::Display for Libc {
}
}

bun_core::comptime_string_map! {
/// `--target` segments that pick a libc; only meaningful together with linux.
static LIBC_NAMES: Libc = {
b"glibc" => Libc::Default,
b"musl" => Libc::Musl,
b"android" => Libc::Android,
};
}

struct BaselineFormatter {
baseline: bool,
}
Expand All @@ -90,12 +99,49 @@ impl fmt::Display for BaselineFormatter {
}
}

#[derive(thiserror::Error, Debug, strum::IntoStaticStr)]
pub enum ParseError {
#[error("UnsupportedTarget")]
UnsupportedTarget,
#[error("InvalidTarget")]
InvalidTarget,
/// Why a `--target` string was rejected. `Display` is the CLI error message.
#[derive(thiserror::Error, Debug, Clone, Copy)]
pub enum ParseError<'a> {
/// `segment` of `input` is not an architecture, OS, CPU tier, libc or version.
UnsupportedSegment {
segment: &'a [u8],
input: &'a [u8],
},
/// A `-vX.Y.Z` segment missing one of its three components.
IncompleteVersion,
/// A libc segment was combined with a non-linux OS.
LibcRequiresLinux(Libc),
Wasm,
}

impl fmt::Display for ParseError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ParseError::UnsupportedSegment { segment, input } => write!(
f,
"Unsupported target {} in \"bun{}\"\n\
To see the supported targets:\n \
https://bun.com/docs/bundler/executables",
bun_fmt::quote(segment),
bstr::BStr::new(input),
),
ParseError::IncompleteVersion => write!(
f,
"Please pass a complete version number to --target. For example, --target=bun-v{}",
Environment::VERSION_STRING,
),
ParseError::LibcRequiresLinux(Libc::Default) => {
f.write_str("invalid target, glibc only exists on linux")
}
ParseError::LibcRequiresLinux(Libc::Musl) => {
f.write_str("invalid target, musl libc only exists on linux")
}
ParseError::LibcRequiresLinux(Libc::Android) => f.write_str(
"invalid target, android only exists with linux (use bun-linux-arm64-android)",
),
ParseError::Wasm => f.write_str("invalid target, WebAssembly is not supported. Sorry!"),
}
}
}

impl CompileTarget {
Expand Down Expand Up @@ -238,7 +284,7 @@ impl CompileTarget {
}
}

pub fn try_from(input_: &[u8]) -> Result<CompileTarget, ParseError> {
pub fn try_from(input_: &[u8]) -> Result<CompileTarget, ParseError<'_>> {
let mut this = CompileTarget::default();
let input = strings::trim(input_, b" \t\r");
if input.is_empty() {
Expand Down Expand Up @@ -284,7 +330,7 @@ impl CompileTarget {
|| version.version.minor.is_none()
|| version.version.patch.is_none()
{
return Err(ParseError::InvalidTarget);
return Err(ParseError::IncompleteVersion);
}

this.version = Version {
Expand All @@ -297,16 +343,15 @@ impl CompileTarget {
_found_version = true;
continue;
}
} else if token == b"musl" {
this.libc = Libc::Musl;
found_libc = true;
continue;
} else if token == b"android" {
this.libc = Libc::Android;
} else if let Some(libc) = LIBC_NAMES.get(token) {
this.libc = *libc;
found_libc = true;
continue;
} else {
return Err(ParseError::UnsupportedTarget);
return Err(ParseError::UnsupportedSegment {
segment: token,
input: input_,
});
}
}

Expand All @@ -328,12 +373,13 @@ impl CompileTarget {
this.baseline = false;
}

if this.libc != Libc::Default && this.os != OperatingSystem::Linux {
return Err(ParseError::InvalidTarget);
// Not `libc != Default`: an explicit "glibc" parses to `Default` and is just as invalid off linux.
if found_libc && this.os != OperatingSystem::Linux {
return Err(ParseError::LibcRequiresLinux(this.libc));
}

if this.arch == Architecture::Wasm || this.os == OperatingSystem::Wasm {
return Err(ParseError::InvalidTarget);
return Err(ParseError::Wasm);
}

Ok(this)
Expand All @@ -342,61 +388,8 @@ impl CompileTarget {
pub fn from(input_: &[u8]) -> CompileTarget {
match Self::try_from(input_) {
Ok(t) => t,
Err(ParseError::UnsupportedTarget) => {
let input = strings::trim(input_, b" \t\r");
let mut splitter = strings::split(input, b"-");
let mut unsupported_token: Option<&[u8]> = None;
while let Some(token) = splitter.next() {
if token.is_empty() {
continue;
}
if ARCHITECTURE_NAMES.get(token).is_none()
&& OPERATING_SYSTEM_NAMES.get(token).is_none()
&& token != b"modern"
&& token != b"baseline"
&& token != b"musl"
&& token != b"android"
&& !(strings::has_prefix(token, b"v1.")
|| strings::has_prefix(token, b"v0."))
{
unsupported_token = Some(token);
break;
}
}

if let Some(token) = unsupported_token {
bun_core::err_generic!(
"Unsupported target {} in \"bun{}\"\n\
To see the supported targets:\n \
https://bun.com/docs/bundler/executables",
bun_fmt::quote(token),
bstr::BStr::new(input_),
);
} else {
bun_core::err_generic!("Unsupported target: {}", bstr::BStr::new(input_));
}
Global::exit(1);
}
Err(ParseError::InvalidTarget) => {
let input = strings::trim(input_, b" \t\r");
if strings::contains(input, b"musl") && !strings::contains(input, b"linux") {
bun_core::err_generic!("invalid target, musl libc only exists on linux");
} else if strings::contains(input, b"android")
&& !strings::contains(input, b"linux")
{
bun_core::err_generic!(
"invalid target, android only exists with linux (use bun-linux-arm64-android)"
);
} else if strings::contains(input, b"wasm") {
bun_core::err_generic!("invalid target, WebAssembly is not supported. Sorry!");
} else if strings::contains(input, b"v") {
bun_core::err_generic!(
"Please pass a complete version number to --target. For example, --target=bun-v{}",
Environment::VERSION_STRING,
);
} else {
bun_core::err_generic!("Invalid target: {}", bstr::BStr::new(input_));
}
Err(err) => {
bun_core::err_generic!("{}", err);
Global::exit(1);
}
}
Expand Down
Loading