mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-01 23:59:42 +00:00
Merge pull request #5814 from roc-lang/aarch-more-num-tests
aarch64 on macos
This commit is contained in:
commit
c79ad40aea
4 changed files with 298 additions and 85 deletions
|
|
@ -41,6 +41,9 @@ jobs:
|
||||||
- name: check that the platform`s produced dylib is loadable
|
- name: check that the platform`s produced dylib is loadable
|
||||||
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked
|
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked
|
||||||
|
|
||||||
|
- name: test aarch64 dev backend
|
||||||
|
run: nix develop -c cargo test-gen-dev bitwise_and --locked --release
|
||||||
|
|
||||||
# we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-target
|
# we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-target
|
||||||
- name: execute llvm wasm tests with --release
|
- name: execute llvm wasm tests with --release
|
||||||
run: nix develop -c cargo test-gen-llvm-wasm --locked --release
|
run: nix develop -c cargo test-gen-llvm-wasm --locked --release
|
||||||
|
|
|
||||||
|
|
@ -52,16 +52,46 @@ pub enum AArch64GeneralReg {
|
||||||
ZRSP = 31,
|
ZRSP = 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegTrait for AArch64GeneralReg {
|
impl AArch64GeneralReg {
|
||||||
fn value(&self) -> u8 {
|
#[cfg(test)]
|
||||||
*self as u8
|
const fn as_str_32bit(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
AArch64GeneralReg::X0 => "w0",
|
||||||
|
AArch64GeneralReg::X1 => "w1",
|
||||||
|
AArch64GeneralReg::X2 => "w2",
|
||||||
|
AArch64GeneralReg::X3 => "w3",
|
||||||
|
AArch64GeneralReg::X4 => "w4",
|
||||||
|
AArch64GeneralReg::X5 => "w5",
|
||||||
|
AArch64GeneralReg::X6 => "w6",
|
||||||
|
AArch64GeneralReg::X7 => "w7",
|
||||||
|
AArch64GeneralReg::XR => "wr",
|
||||||
|
AArch64GeneralReg::X9 => "w9",
|
||||||
|
AArch64GeneralReg::X10 => "w10",
|
||||||
|
AArch64GeneralReg::X11 => "w11",
|
||||||
|
AArch64GeneralReg::X12 => "w12",
|
||||||
|
AArch64GeneralReg::X13 => "w13",
|
||||||
|
AArch64GeneralReg::X14 => "w14",
|
||||||
|
AArch64GeneralReg::X15 => "w15",
|
||||||
|
AArch64GeneralReg::IP0 => "ip0",
|
||||||
|
AArch64GeneralReg::IP1 => "ip1",
|
||||||
|
AArch64GeneralReg::PR => "pr",
|
||||||
|
AArch64GeneralReg::X19 => "w19",
|
||||||
|
AArch64GeneralReg::X20 => "w20",
|
||||||
|
AArch64GeneralReg::X21 => "w21",
|
||||||
|
AArch64GeneralReg::X22 => "w22",
|
||||||
|
AArch64GeneralReg::X23 => "w23",
|
||||||
|
AArch64GeneralReg::X24 => "w24",
|
||||||
|
AArch64GeneralReg::X25 => "w25",
|
||||||
|
AArch64GeneralReg::X26 => "w26",
|
||||||
|
AArch64GeneralReg::X27 => "w27",
|
||||||
|
AArch64GeneralReg::X28 => "w28",
|
||||||
|
AArch64GeneralReg::FP => "fp",
|
||||||
|
AArch64GeneralReg::LR => "lr",
|
||||||
|
AArch64GeneralReg::ZRSP => "zrsp",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for AArch64GeneralReg {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
const fn as_str_64bit(&self) -> &str {
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match self {
|
match self {
|
||||||
AArch64GeneralReg::X0 => "x0",
|
AArch64GeneralReg::X0 => "x0",
|
||||||
AArch64GeneralReg::X1 => "x1",
|
AArch64GeneralReg::X1 => "x1",
|
||||||
|
|
@ -96,7 +126,18 @@ impl std::fmt::Display for AArch64GeneralReg {
|
||||||
AArch64GeneralReg::LR => "lr",
|
AArch64GeneralReg::LR => "lr",
|
||||||
AArch64GeneralReg::ZRSP => "zrsp",
|
AArch64GeneralReg::ZRSP => "zrsp",
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegTrait for AArch64GeneralReg {
|
||||||
|
fn value(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for AArch64GeneralReg {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str_64bit())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1201,12 +1242,12 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movsx_reg_reg(
|
fn movsx_reg_reg(
|
||||||
_buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
_input_width: RegisterWidth,
|
input_width: RegisterWidth,
|
||||||
_dst: AArch64GeneralReg,
|
dst: AArch64GeneralReg,
|
||||||
_src: AArch64GeneralReg,
|
src: AArch64GeneralReg,
|
||||||
) {
|
) {
|
||||||
todo!("move with sign extension");
|
sign_extend(buf, input_width, dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
@ -1322,11 +1363,15 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
dst: AArch64GeneralReg,
|
dst: AArch64GeneralReg,
|
||||||
offset: i32,
|
offset: i32,
|
||||||
) {
|
) {
|
||||||
|
use RegisterWidth::*;
|
||||||
|
|
||||||
|
// move to destination (zero extends)
|
||||||
|
Self::mov_reg_base32(buf, register_width, dst, offset);
|
||||||
|
|
||||||
|
// then sign-extend if needed
|
||||||
match register_width {
|
match register_width {
|
||||||
RegisterWidth::W8 => todo!("sign extend 1 byte values"),
|
W8 | W16 | W32 => sign_extend(buf, register_width, dst, dst),
|
||||||
RegisterWidth::W16 => todo!("sign extend 2 byte values"),
|
W64 => { /* do nothing */ }
|
||||||
RegisterWidth::W32 => todo!("sign extend 4 byte values"),
|
|
||||||
RegisterWidth::W64 => Self::mov_reg64_base32(buf, dst, offset),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1337,11 +1382,15 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
dst: AArch64GeneralReg,
|
dst: AArch64GeneralReg,
|
||||||
offset: i32,
|
offset: i32,
|
||||||
) {
|
) {
|
||||||
|
use RegisterWidth::*;
|
||||||
|
|
||||||
|
// move to destination (zero extends)
|
||||||
|
Self::mov_reg_base32(buf, register_width, dst, offset);
|
||||||
|
|
||||||
|
// then sign-extend if needed
|
||||||
match register_width {
|
match register_width {
|
||||||
RegisterWidth::W8 => todo!("zero extend 1 byte values"),
|
W8 | W16 => zero_extend(buf, register_width, dst, dst),
|
||||||
RegisterWidth::W16 => todo!("zero extend 2 byte values"),
|
W32 | W64 => { /* do nothing */ }
|
||||||
RegisterWidth::W32 => todo!("zero extend 4 byte values"),
|
|
||||||
RegisterWidth::W64 => Self::mov_reg64_base32(buf, dst, offset),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2267,6 +2316,75 @@ impl UnconditionalBranchImmediate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PackedStruct, Debug)]
|
||||||
|
#[packed_struct(endian = "msb")]
|
||||||
|
pub struct SignExtend {
|
||||||
|
sf: Integer<u8, packed_bits::Bits<1>>,
|
||||||
|
opc: Integer<u8, packed_bits::Bits<2>>,
|
||||||
|
fixed: Integer<u8, packed_bits::Bits<6>>,
|
||||||
|
n: Integer<u8, packed_bits::Bits<1>>,
|
||||||
|
immr: Integer<u8, packed_bits::Bits<6>>,
|
||||||
|
imms: Integer<u8, packed_bits::Bits<6>>,
|
||||||
|
rn: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
rd: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for SignExtend {}
|
||||||
|
|
||||||
|
fn sign_extend(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
register_width: RegisterWidth,
|
||||||
|
dst: AArch64GeneralReg,
|
||||||
|
src: AArch64GeneralReg,
|
||||||
|
) {
|
||||||
|
let imms = match register_width {
|
||||||
|
RegisterWidth::W8 => 0b00_0111, // sxtb
|
||||||
|
RegisterWidth::W16 => 0b00_1111, // sxth
|
||||||
|
RegisterWidth::W32 => 0b01_1111, // sxtw
|
||||||
|
RegisterWidth::W64 => return mov_reg64_reg64(buf, dst, src),
|
||||||
|
};
|
||||||
|
|
||||||
|
let inst = SignExtend {
|
||||||
|
sf: 0b1.into(),
|
||||||
|
opc: 0b00.into(),
|
||||||
|
fixed: 0b100110.into(),
|
||||||
|
n: 0b1.into(),
|
||||||
|
immr: 0b00_0000.into(),
|
||||||
|
imms: imms.into(),
|
||||||
|
rn: src.id().into(),
|
||||||
|
rd: dst.id().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.extend(inst.bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero_extend(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
register_width: RegisterWidth,
|
||||||
|
dst: AArch64GeneralReg,
|
||||||
|
src: AArch64GeneralReg,
|
||||||
|
) {
|
||||||
|
let imms = match register_width {
|
||||||
|
RegisterWidth::W8 => 0b00_0111, // uxtb
|
||||||
|
RegisterWidth::W16 => 0b00_1111, // uxth
|
||||||
|
RegisterWidth::W32 => return mov_reg64_reg64(buf, dst, src),
|
||||||
|
RegisterWidth::W64 => return mov_reg64_reg64(buf, dst, src),
|
||||||
|
};
|
||||||
|
|
||||||
|
let inst = SignExtend {
|
||||||
|
sf: 0b0.into(),
|
||||||
|
opc: 0b10.into(),
|
||||||
|
fixed: 0b100110.into(),
|
||||||
|
n: 0b0.into(),
|
||||||
|
immr: 0b00_0000.into(),
|
||||||
|
imms: imms.into(),
|
||||||
|
rn: src.id().into(),
|
||||||
|
rd: dst.id().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.extend(inst.bytes());
|
||||||
|
}
|
||||||
|
|
||||||
// Uses unsigned Offset
|
// Uses unsigned Offset
|
||||||
// opc = 0b01 means load
|
// opc = 0b01 means load
|
||||||
// opc = 0b00 means store
|
// opc = 0b00 means store
|
||||||
|
|
@ -3663,6 +3781,22 @@ mod tests {
|
||||||
_ => format!("{self}"),
|
_ => format!("{self}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn capstone_string_32bit(&self, zrsp_kind: ZRSPKind) -> String {
|
||||||
|
match self {
|
||||||
|
AArch64GeneralReg::XR => "w8".to_owned(),
|
||||||
|
AArch64GeneralReg::IP0 => "w16".to_owned(),
|
||||||
|
AArch64GeneralReg::IP1 => "w17".to_owned(),
|
||||||
|
AArch64GeneralReg::PR => "w18".to_owned(),
|
||||||
|
AArch64GeneralReg::FP => "w29".to_owned(),
|
||||||
|
AArch64GeneralReg::LR => "w30".to_owned(),
|
||||||
|
AArch64GeneralReg::ZRSP => match zrsp_kind {
|
||||||
|
UsesZR => "wzr".to_owned(),
|
||||||
|
UsesSP => "sp".to_owned(),
|
||||||
|
},
|
||||||
|
_ => self.as_str_32bit().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AArch64FloatReg {
|
impl AArch64FloatReg {
|
||||||
|
|
@ -3688,6 +3822,13 @@ mod tests {
|
||||||
//const TEST_I32: i32 = 0x12345678;
|
//const TEST_I32: i32 = 0x12345678;
|
||||||
//const TEST_I64: i64 = 0x12345678_9ABCDEF0;
|
//const TEST_I64: i64 = 0x12345678_9ABCDEF0;
|
||||||
|
|
||||||
|
const ALL_REGISTER_WIDTHS: &[RegisterWidth] = &[
|
||||||
|
RegisterWidth::W8,
|
||||||
|
RegisterWidth::W16,
|
||||||
|
RegisterWidth::W32,
|
||||||
|
RegisterWidth::W64,
|
||||||
|
];
|
||||||
|
|
||||||
const ALL_GENERAL_REGS: &[AArch64GeneralReg] = &[
|
const ALL_GENERAL_REGS: &[AArch64GeneralReg] = &[
|
||||||
AArch64GeneralReg::X0,
|
AArch64GeneralReg::X0,
|
||||||
AArch64GeneralReg::X1,
|
AArch64GeneralReg::X1,
|
||||||
|
|
@ -4822,4 +4963,61 @@ mod tests {
|
||||||
ALL_GENERAL_REGS
|
ALL_GENERAL_REGS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sign_extend() {
|
||||||
|
disassembler_test!(
|
||||||
|
sign_extend,
|
||||||
|
|w, reg1: AArch64GeneralReg, reg2: AArch64GeneralReg| format!(
|
||||||
|
"{} {}, {}",
|
||||||
|
match w {
|
||||||
|
RegisterWidth::W8 => "sxtb",
|
||||||
|
RegisterWidth::W16 => "sxth",
|
||||||
|
RegisterWidth::W32 => "sxtw",
|
||||||
|
RegisterWidth::W64 => "mov",
|
||||||
|
},
|
||||||
|
reg1.capstone_string(UsesZR),
|
||||||
|
match w {
|
||||||
|
RegisterWidth::W8 => reg2.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W16 => reg2.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W32 => reg2.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W64 => reg2.capstone_string(UsesZR),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
ALL_REGISTER_WIDTHS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_extend() {
|
||||||
|
disassembler_test!(
|
||||||
|
zero_extend,
|
||||||
|
|w, reg1: AArch64GeneralReg, reg2: AArch64GeneralReg| format!(
|
||||||
|
"{} {}, {}",
|
||||||
|
match w {
|
||||||
|
RegisterWidth::W8 => "uxtb",
|
||||||
|
RegisterWidth::W16 => "uxth",
|
||||||
|
RegisterWidth::W32 => "mov",
|
||||||
|
RegisterWidth::W64 => "mov",
|
||||||
|
},
|
||||||
|
match w {
|
||||||
|
RegisterWidth::W8 => reg1.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W16 => reg1.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W32 => reg1.capstone_string(UsesZR),
|
||||||
|
RegisterWidth::W64 => reg1.capstone_string(UsesZR),
|
||||||
|
},
|
||||||
|
match w {
|
||||||
|
RegisterWidth::W8 => reg2.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W16 => reg2.capstone_string_32bit(UsesZR),
|
||||||
|
RegisterWidth::W32 => reg2.capstone_string(UsesZR),
|
||||||
|
RegisterWidth::W64 => reg2.capstone_string(UsesZR),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
ALL_REGISTER_WIDTHS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,22 +275,7 @@ fn generate_wrapper<'a, B: Backend<'a>>(
|
||||||
};
|
};
|
||||||
output.add_symbol(symbol);
|
output.add_symbol(symbol);
|
||||||
if let Some(sym_id) = output.symbol_id(name) {
|
if let Some(sym_id) = output.symbol_id(name) {
|
||||||
let (encoding, size) = match backend.target_info().architecture {
|
let reloc = create_relocation(backend.target_info(), sym_id, offset + proc_offset);
|
||||||
roc_target::Architecture::Aarch32 => todo!(),
|
|
||||||
roc_target::Architecture::Aarch64 => (RelocationEncoding::AArch64Call, 26),
|
|
||||||
roc_target::Architecture::Wasm32 => todo!(),
|
|
||||||
roc_target::Architecture::X86_32 => todo!(),
|
|
||||||
roc_target::Architecture::X86_64 => (RelocationEncoding::X86Branch, 32),
|
|
||||||
};
|
|
||||||
|
|
||||||
let reloc = write::Relocation {
|
|
||||||
offset: offset + proc_offset,
|
|
||||||
size,
|
|
||||||
kind: RelocationKind::PltRelative,
|
|
||||||
encoding,
|
|
||||||
symbol: sym_id,
|
|
||||||
addend: -4,
|
|
||||||
};
|
|
||||||
|
|
||||||
match output.add_relocation(text_section, reloc) {
|
match output.add_relocation(text_section, reloc) {
|
||||||
Ok(obj) => obj,
|
Ok(obj) => obj,
|
||||||
|
|
@ -301,6 +286,49 @@ fn generate_wrapper<'a, B: Backend<'a>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_relocation(target_info: TargetInfo, symbol: SymbolId, offset: u64) -> write::Relocation {
|
||||||
|
let (encoding, size, addend, kind) = match target_info.architecture {
|
||||||
|
roc_target::Architecture::Aarch32 => todo!(),
|
||||||
|
roc_target::Architecture::Aarch64 => {
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
(
|
||||||
|
RelocationEncoding::Generic,
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
RelocationKind::MachO {
|
||||||
|
value: 2,
|
||||||
|
relative: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
RelocationEncoding::AArch64Call,
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
RelocationKind::PltRelative,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roc_target::Architecture::Wasm32 => todo!(),
|
||||||
|
roc_target::Architecture::X86_32 => todo!(),
|
||||||
|
roc_target::Architecture::X86_64 => (
|
||||||
|
RelocationEncoding::X86Branch,
|
||||||
|
32,
|
||||||
|
-4,
|
||||||
|
RelocationKind::PltRelative,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
write::Relocation {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
kind,
|
||||||
|
encoding,
|
||||||
|
symbol,
|
||||||
|
addend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_object<'a, B: Backend<'a>>(
|
fn build_object<'a, B: Backend<'a>>(
|
||||||
procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
mut backend: B,
|
mut backend: B,
|
||||||
|
|
@ -891,22 +919,7 @@ fn build_proc<'a, B: Backend<'a>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sym_id) = output.symbol_id(name.as_bytes()) {
|
if let Some(sym_id) = output.symbol_id(name.as_bytes()) {
|
||||||
let (encoding, size) = match target_info.architecture {
|
create_relocation(target_info, sym_id, offset + proc_offset)
|
||||||
roc_target::Architecture::Aarch32 => todo!(),
|
|
||||||
roc_target::Architecture::Aarch64 => (RelocationEncoding::AArch64Call, 26),
|
|
||||||
roc_target::Architecture::Wasm32 => todo!(),
|
|
||||||
roc_target::Architecture::X86_32 => todo!(),
|
|
||||||
roc_target::Architecture::X86_64 => (RelocationEncoding::X86Branch, 32),
|
|
||||||
};
|
|
||||||
|
|
||||||
write::Relocation {
|
|
||||||
offset: offset + proc_offset,
|
|
||||||
size,
|
|
||||||
kind: RelocationKind::PltRelative,
|
|
||||||
encoding,
|
|
||||||
symbol: sym_id,
|
|
||||||
addend: -4,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
internal_error!("failed to find fn symbol for {:?}", name);
|
internal_error!("failed to find fn symbol for {:?}", name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -215,8 +215,7 @@ pub fn helper(
|
||||||
let builtins_host_tempfile =
|
let builtins_host_tempfile =
|
||||||
roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile");
|
roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile");
|
||||||
|
|
||||||
// TODO make this an environment variable
|
if std::env::var("ROC_DEV_WRITE_OBJ").is_ok() {
|
||||||
if false {
|
|
||||||
let file_path = std::env::temp_dir().join("app.o");
|
let file_path = std::env::temp_dir().join("app.o");
|
||||||
println!("gen-test object file written to {}", file_path.display());
|
println!("gen-test object file written to {}", file_path.display());
|
||||||
std::fs::copy(&app_o_file, file_path).unwrap();
|
std::fs::copy(&app_o_file, file_path).unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue