mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Merge remote-tracking branch 'remote/main' into list-splitting
This commit is contained in:
commit
69c2fef943
244 changed files with 3601 additions and 5671 deletions
|
@ -42,29 +42,6 @@ pub fn link(
|
|||
}
|
||||
}
|
||||
|
||||
/// Same format as the precompiled host filename, except with a file extension like ".o" or ".obj"
|
||||
pub fn legacy_host_file(target: Target, platform_main_roc: &Path) -> PathBuf {
|
||||
let lib_ext = target.static_library_file_ext();
|
||||
|
||||
let file_name = roc_linker::preprocessed_host_filename(target)
|
||||
.replace(roc_linker::PRECOMPILED_HOST_EXT, lib_ext);
|
||||
|
||||
let lib_path = platform_main_roc.with_file_name(file_name);
|
||||
|
||||
let default_host_path: PathBuf = platform_main_roc
|
||||
.with_file_name("libhost")
|
||||
.with_extension(lib_ext);
|
||||
|
||||
if lib_path.exists() {
|
||||
lib_path
|
||||
} else if default_host_path.exists() {
|
||||
default_host_path
|
||||
} else {
|
||||
let obj_ext = target.object_file_ext();
|
||||
lib_path.with_extension(obj_ext)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to find a file that is stored relative to the roc executable.
|
||||
// Since roc is built in target/debug/roc, we may need to drop that path to find the file.
|
||||
// This is used to avoid depending on the current working directory.
|
||||
|
@ -457,6 +434,7 @@ pub fn rebuild_host(
|
|||
};
|
||||
|
||||
let host_dest = if matches!(target.architecture(), Architecture::Wasm32) {
|
||||
// TODO verify this is corect, how do we do get here with OptLevel::Development
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
platform_main_roc.with_extension("o")
|
||||
} else {
|
||||
|
@ -467,7 +445,7 @@ pub fn rebuild_host(
|
|||
.with_file_name("dynhost")
|
||||
.with_extension(executable_extension)
|
||||
} else {
|
||||
legacy_host_file(target, platform_main_roc)
|
||||
platform_main_roc.with_file_name(target.prebuilt_static_object())
|
||||
};
|
||||
|
||||
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
|
||||
|
@ -1352,9 +1330,9 @@ pub fn llvm_module_to_dylib(
|
|||
unsafe { Library::new(path) }
|
||||
}
|
||||
|
||||
pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &Path) {
|
||||
pub fn preprocess_host_wasm32(host_input_path: &Path, host_output_path: &Path) {
|
||||
let host_input = host_input_path.to_str().unwrap();
|
||||
let output_file = preprocessed_host_path.to_str().unwrap();
|
||||
let output_file = host_output_path.to_str().unwrap();
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
@ -1400,9 +1378,11 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P
|
|||
fn run_build_command(mut command: Command, file_to_build: &str, flaky_fail_counter: usize) {
|
||||
let command_string = stringify_command(&command, false);
|
||||
let cmd_str = &command_string;
|
||||
|
||||
roc_debug_flags::dbg_do!(roc_debug_flags::ROC_PRINT_BUILD_COMMANDS, {
|
||||
print_command_str(cmd_str);
|
||||
});
|
||||
|
||||
let cmd_output = command.output().unwrap();
|
||||
let max_flaky_fail_count = 10;
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use crate::link::{
|
||||
legacy_host_file, link, preprocess_host_wasm32, rebuild_host, LinkType, LinkingStrategy,
|
||||
};
|
||||
use crate::link::{link, preprocess_host_wasm32, rebuild_host, LinkType, LinkingStrategy};
|
||||
use bumpalo::collections::CollectIn;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::memory_buffer::MemoryBuffer;
|
||||
|
@ -28,6 +26,7 @@ use std::{
|
|||
|
||||
#[cfg(feature = "target-wasm32")]
|
||||
use roc_collections::all::MutSet;
|
||||
use roc_target::SurgicalHostArtifacts;
|
||||
|
||||
pub const DEFAULT_ROC_FILENAME: &str = "main.roc";
|
||||
|
||||
|
@ -97,7 +96,7 @@ pub fn gen_from_mono_module<'a>(
|
|||
roc_file_path: &Path,
|
||||
target: Target,
|
||||
code_gen_options: CodeGenOptions,
|
||||
preprocessed_host_path: &Path,
|
||||
built_host_opt: &BuiltHostOpt,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
) -> GenFromMono<'a> {
|
||||
let path = roc_file_path;
|
||||
|
@ -107,19 +106,27 @@ pub fn gen_from_mono_module<'a>(
|
|||
let opt = code_gen_options.opt_level;
|
||||
|
||||
match code_gen_options.backend {
|
||||
CodeGenBackend::Wasm => gen_from_mono_module_dev(
|
||||
arena,
|
||||
loaded,
|
||||
target,
|
||||
preprocessed_host_path,
|
||||
wasm_dev_stack_bytes,
|
||||
AssemblyBackendMode::Binary, // dummy value, unused in practice
|
||||
),
|
||||
CodeGenBackend::Wasm => {
|
||||
assert_ne!(
|
||||
*built_host_opt,
|
||||
BuiltHostOpt::None,
|
||||
"Wasm backend needs a built host."
|
||||
);
|
||||
|
||||
gen_from_mono_module_dev(
|
||||
arena,
|
||||
loaded,
|
||||
target,
|
||||
built_host_opt,
|
||||
wasm_dev_stack_bytes,
|
||||
AssemblyBackendMode::Binary, // dummy value, unused in practice
|
||||
)
|
||||
}
|
||||
CodeGenBackend::Assembly(backend_mode) => gen_from_mono_module_dev(
|
||||
arena,
|
||||
loaded,
|
||||
target,
|
||||
preprocessed_host_path,
|
||||
built_host_opt,
|
||||
wasm_dev_stack_bytes,
|
||||
backend_mode,
|
||||
),
|
||||
|
@ -430,43 +437,59 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-wasm32")]
|
||||
fn gen_from_mono_module_dev<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
loaded: MonomorphizedModule<'a>,
|
||||
target: Target,
|
||||
preprocessed_host_path: &Path,
|
||||
built_host_opt: &BuiltHostOpt,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
backend_mode: AssemblyBackendMode,
|
||||
#[allow(unused_variables)] backend_mode: AssemblyBackendMode,
|
||||
) -> GenFromMono<'a> {
|
||||
match target.architecture() {
|
||||
Architecture::Wasm32 => gen_from_mono_module_dev_wasm32(
|
||||
arena,
|
||||
loaded,
|
||||
preprocessed_host_path,
|
||||
wasm_dev_stack_bytes,
|
||||
),
|
||||
Architecture::X86_64 | Architecture::Aarch64 => {
|
||||
gen_from_mono_module_dev_assembly(arena, loaded, target, backend_mode)
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
match (built_host_opt, target.architecture()) {
|
||||
(BuiltHostOpt::Additive(host_path), Architecture::Wasm32) => {
|
||||
#[cfg(feature = "target-wasm32")]
|
||||
{
|
||||
gen_from_mono_module_dev_wasm32(arena, loaded, host_path, wasm_dev_stack_bytes)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "target-wasm32"))]
|
||||
pub fn gen_from_mono_module_dev<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
loaded: MonomorphizedModule<'a>,
|
||||
target: Target,
|
||||
_host_input_path: &Path,
|
||||
_wasm_dev_stack_bytes: Option<u32>,
|
||||
backend_mode: AssemblyBackendMode,
|
||||
) -> GenFromMono<'a> {
|
||||
match target.architecture() {
|
||||
Architecture::X86_64 | Architecture::Aarch64 => {
|
||||
gen_from_mono_module_dev_assembly(arena, loaded, target, backend_mode)
|
||||
#[cfg(not(feature = "target-wasm32"))]
|
||||
{
|
||||
internal_error!("Compiler was not built with feature 'target-wasm32'.");
|
||||
}
|
||||
}
|
||||
(BuiltHostOpt::None, Architecture::Wasm32) => {
|
||||
internal_error!("Cannot compile wasm32 without a host on the dev compiler backend.")
|
||||
}
|
||||
(BuiltHostOpt::Legacy(host_path), Architecture::Wasm32) => internal_error!(
|
||||
"Unsupported host files found for use with wasm32 dev compiler backend:\n {}",
|
||||
host_path.display()
|
||||
),
|
||||
(
|
||||
BuiltHostOpt::Surgical(SurgicalHostArtifacts {
|
||||
preprocessed_host, ..
|
||||
}),
|
||||
Architecture::Wasm32,
|
||||
) => internal_error!(
|
||||
"Unsupported host files found for use with wasm32 dev compiler backend:\n {}",
|
||||
preprocessed_host.display()
|
||||
),
|
||||
(_, Architecture::X86_64 | Architecture::Aarch64) => {
|
||||
#[cfg(not(feature = "target-wasm32"))]
|
||||
{
|
||||
gen_from_mono_module_dev_assembly(arena, loaded, target, backend_mode)
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-wasm32")]
|
||||
{
|
||||
internal_error!("Compiler was not built with feature 'target-wasm32'.")
|
||||
}
|
||||
}
|
||||
(_, Architecture::Aarch32) => {
|
||||
internal_error!("Dev compiler backend does not support 32 bit ARM architectures")
|
||||
}
|
||||
(_, Architecture::X86_32) => {
|
||||
internal_error!("Dev compiler backend does not support 32 bit x86 architectures")
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +497,7 @@ pub fn gen_from_mono_module_dev<'a>(
|
|||
fn gen_from_mono_module_dev_wasm32<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
loaded: MonomorphizedModule<'a>,
|
||||
preprocessed_host_path: &Path,
|
||||
built_host_path: &Path,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
) -> GenFromMono<'a> {
|
||||
let all_code_gen_start = Instant::now();
|
||||
|
@ -500,17 +523,17 @@ fn gen_from_mono_module_dev_wasm32<'a>(
|
|||
stack_bytes: wasm_dev_stack_bytes.unwrap_or(roc_gen_wasm::Env::DEFAULT_STACK_BYTES),
|
||||
};
|
||||
|
||||
let host_bytes = std::fs::read(preprocessed_host_path).unwrap_or_else(|_| {
|
||||
let host_bytes = std::fs::read(built_host_path).unwrap_or_else(|_| {
|
||||
internal_error!(
|
||||
"Failed to read host object file {}! Try omitting --prebuilt-platform",
|
||||
preprocessed_host_path.display()
|
||||
built_host_path.display()
|
||||
)
|
||||
});
|
||||
|
||||
let host_module = roc_gen_wasm::parse_host(arena, &host_bytes).unwrap_or_else(|e| {
|
||||
internal_error!(
|
||||
"I ran into a problem with the host object file, {} at offset 0x{:x}:\n{}",
|
||||
preprocessed_host_path.display(),
|
||||
built_host_path.display(),
|
||||
e.offset,
|
||||
e.message
|
||||
)
|
||||
|
@ -544,6 +567,7 @@ fn gen_from_mono_module_dev_wasm32<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn gen_from_mono_module_dev_assembly<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
loaded: MonomorphizedModule<'a>,
|
||||
|
@ -720,7 +744,8 @@ pub fn build_file<'a>(
|
|||
emit_timings: bool,
|
||||
link_type: LinkType,
|
||||
linking_strategy: LinkingStrategy,
|
||||
prebuilt_requested: bool,
|
||||
build_host: bool,
|
||||
suppress_build_host_warning: bool,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
load_config: LoadConfig,
|
||||
|
@ -728,7 +753,6 @@ pub fn build_file<'a>(
|
|||
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
|
||||
let compilation_start = Instant::now();
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let loaded = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
app_module_path.clone(),
|
||||
|
@ -746,7 +770,8 @@ pub fn build_file<'a>(
|
|||
emit_timings,
|
||||
link_type,
|
||||
linking_strategy,
|
||||
prebuilt_requested,
|
||||
build_host,
|
||||
suppress_build_host_warning,
|
||||
wasm_dev_stack_bytes,
|
||||
loaded,
|
||||
compilation_start,
|
||||
|
@ -754,6 +779,64 @@ pub fn build_file<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
/// Opt because of possible None value
|
||||
// Advice: do not try to wrap this in an Option, that would require cloning in build_loaded_file.
|
||||
pub enum BuiltHostOpt {
|
||||
Additive(PathBuf),
|
||||
Legacy(PathBuf),
|
||||
// SurgicalHostArtifacts contains metadata, preprocessed_host
|
||||
Surgical(SurgicalHostArtifacts),
|
||||
None,
|
||||
}
|
||||
|
||||
fn build_and_preprocess_host(
|
||||
code_gen_options: CodeGenOptions,
|
||||
dll_stub_symbols: Vec<String>,
|
||||
emit_timings: bool,
|
||||
linking_strategy: LinkingStrategy,
|
||||
platform_main_roc: &Path,
|
||||
preprocessed_host_path: &Path,
|
||||
target: Target,
|
||||
) -> BuiltHostOpt {
|
||||
let rebuild_thread = match linking_strategy {
|
||||
LinkingStrategy::Additive => spawn_wasm32_host_build_thread(
|
||||
code_gen_options.opt_level,
|
||||
target,
|
||||
platform_main_roc.to_owned(),
|
||||
preprocessed_host_path.to_owned(),
|
||||
),
|
||||
LinkingStrategy::Surgical => {
|
||||
let preprocessed_path =
|
||||
platform_main_roc.with_file_name(target.prebuilt_surgical_host());
|
||||
let metadata_path = platform_main_roc.with_file_name(target.metadata_file_name());
|
||||
|
||||
spawn_surgical_host_build_thread(
|
||||
code_gen_options.opt_level,
|
||||
target,
|
||||
platform_main_roc.to_owned(),
|
||||
dll_stub_symbols,
|
||||
preprocessed_path,
|
||||
preprocessed_host_path.to_owned(),
|
||||
metadata_path,
|
||||
)
|
||||
}
|
||||
LinkingStrategy::Legacy => spawn_legacy_host_build_thread(
|
||||
code_gen_options.opt_level,
|
||||
target,
|
||||
platform_main_roc.to_owned(),
|
||||
),
|
||||
};
|
||||
let (rebuild_duration, path) = rebuild_thread.join().expect("Failed to build host.");
|
||||
if emit_timings {
|
||||
println!(
|
||||
"Finished rebuilding the platform host in {} ms\n",
|
||||
rebuild_duration
|
||||
);
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_loaded_file<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -762,121 +845,59 @@ fn build_loaded_file<'a>(
|
|||
code_gen_options: CodeGenOptions,
|
||||
emit_timings: bool,
|
||||
link_type: LinkType,
|
||||
mut linking_strategy: LinkingStrategy,
|
||||
prebuilt_requested: bool,
|
||||
linking_strategy: LinkingStrategy,
|
||||
build_host_requested: bool,
|
||||
suppress_build_host_warning: bool,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
loaded: roc_load::MonomorphizedModule<'a>,
|
||||
compilation_start: Instant,
|
||||
out_path: Option<&Path>,
|
||||
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
|
||||
let platform_main_roc = match &loaded.entry_point {
|
||||
// get the platform path from the app header
|
||||
let platform_main_roc_path = match &loaded.entry_point {
|
||||
EntryPoint::Executable { platform_path, .. } => platform_path.to_path_buf(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// For example, if we're loading the platform from a URL, it's automatically prebuilt
|
||||
// even if the --prebuilt-platform CLI flag wasn't set.
|
||||
let is_platform_prebuilt = prebuilt_requested || loaded.uses_prebuilt_platform;
|
||||
let output_exe_path = get_exe_path(
|
||||
out_path,
|
||||
app_module_path.as_path(),
|
||||
target,
|
||||
linking_strategy,
|
||||
link_type,
|
||||
);
|
||||
|
||||
if is_platform_prebuilt && linking_strategy == LinkingStrategy::Surgical {
|
||||
// Fallback to legacy linking if the preprocessed host file does not exist, but a legacy host does exist.
|
||||
let preprocessed_host_path =
|
||||
platform_main_roc.with_file_name(roc_linker::preprocessed_host_filename(target));
|
||||
let legacy_host_path = legacy_host_file(target, &platform_main_roc);
|
||||
if !preprocessed_host_path.exists() && legacy_host_path.exists() {
|
||||
linking_strategy = LinkingStrategy::Legacy;
|
||||
}
|
||||
}
|
||||
let dll_stub_symbols =
|
||||
roc_linker::ExposedSymbols::from_exposed_to_host(&loaded.interns, &loaded.exposed_to_host);
|
||||
|
||||
// the preprocessed host is stored beside the platform's main.roc
|
||||
let preprocessed_host_path = if linking_strategy == LinkingStrategy::Legacy {
|
||||
if target == Target::Wasm32 {
|
||||
// when compiling a wasm application, we implicitly assume here that the host is in zig
|
||||
// and has a file called "host.zig"
|
||||
platform_main_roc.with_file_name("host.zig")
|
||||
let built_host_opt =
|
||||
// Not sure if this is correct for all calls with LinkType::Dylib...
|
||||
if link_type == LinkType::Dylib || target == Target::Wasm32 {
|
||||
BuiltHostOpt::None
|
||||
} else {
|
||||
legacy_host_file(target, &platform_main_roc)
|
||||
}
|
||||
} else {
|
||||
platform_main_roc.with_file_name(roc_linker::preprocessed_host_filename(target))
|
||||
};
|
||||
let prebuilt_host = determine_built_host_path(&platform_main_roc_path, target, build_host_requested, link_type, linking_strategy, suppress_build_host_warning);
|
||||
|
||||
let output_exe_path = match out_path {
|
||||
Some(path) => {
|
||||
// true iff the path ends with a directory separator,
|
||||
// e.g. '/' on UNIX, '/' or '\\' on Windows
|
||||
let ends_with_sep = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
path.as_os_str().as_bytes().ends_with(&[b'/'])
|
||||
match prebuilt_host {
|
||||
BuiltHostOpt::None => {
|
||||
build_and_preprocess_host(
|
||||
code_gen_options,
|
||||
dll_stub_symbols,
|
||||
emit_timings,
|
||||
linking_strategy,
|
||||
&platform_main_roc_path,
|
||||
&output_exe_path,
|
||||
target,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
let last = path.as_os_str().encode_wide().last();
|
||||
|
||||
last == Some(0x002f)// UTF-16 slash
|
||||
|| last == Some(0x005c) // UTF-16 backslash
|
||||
BuiltHostOpt::Surgical(ref surgical_artifacts) => {
|
||||
// Copy preprocessed host to executable location.
|
||||
// The surgical linker will modify that copy in-place.
|
||||
std::fs::copy(&surgical_artifacts.preprocessed_host, output_exe_path.as_path()).unwrap();
|
||||
prebuilt_host
|
||||
}
|
||||
};
|
||||
|
||||
// If you specified a path that ends in in a directory separator, then
|
||||
// use that directory, but use the app module's filename for the filename.
|
||||
if ends_with_sep {
|
||||
let filename = app_module_path.file_name().unwrap_or_default();
|
||||
|
||||
with_output_extension(&path.join(filename), target, linking_strategy, link_type)
|
||||
} else {
|
||||
path.to_path_buf()
|
||||
other => other
|
||||
}
|
||||
}
|
||||
None => with_output_extension(&app_module_path, target, linking_strategy, link_type),
|
||||
};
|
||||
|
||||
// We don't need to spawn a rebuild thread when using a prebuilt host.
|
||||
let rebuild_thread = if matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||
None
|
||||
} else if is_platform_prebuilt {
|
||||
if !preprocessed_host_path.exists() {
|
||||
invalid_prebuilt_platform(prebuilt_requested, preprocessed_host_path);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if linking_strategy == LinkingStrategy::Surgical {
|
||||
// Copy preprocessed host to executable location.
|
||||
// The surgical linker will modify that copy in-place.
|
||||
std::fs::copy(&preprocessed_host_path, output_exe_path.as_path()).unwrap();
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
// TODO this should probably be moved before load_and_monomorphize.
|
||||
// To do this we will need to preprocess files just for their exported symbols.
|
||||
// Also, we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get prebuilt platforms from there.
|
||||
|
||||
let dll_stub_symbols = roc_linker::ExposedSymbols::from_exposed_to_host(
|
||||
&loaded.interns,
|
||||
&loaded.exposed_to_host,
|
||||
);
|
||||
|
||||
let join_handle = spawn_rebuild_thread(
|
||||
code_gen_options.opt_level,
|
||||
linking_strategy,
|
||||
platform_main_roc.clone(),
|
||||
preprocessed_host_path.clone(),
|
||||
output_exe_path.clone(),
|
||||
target,
|
||||
dll_stub_symbols,
|
||||
);
|
||||
|
||||
Some(join_handle)
|
||||
};
|
||||
};
|
||||
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
|
@ -909,31 +930,13 @@ fn build_loaded_file<'a>(
|
|||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
let loaded = loaded;
|
||||
|
||||
let opt_rebuild_timing = if let Some(rebuild_thread) = rebuild_thread {
|
||||
if linking_strategy == LinkingStrategy::Additive {
|
||||
let rebuild_duration = rebuild_thread
|
||||
.join()
|
||||
.expect("Failed to (re)build platform.");
|
||||
|
||||
if emit_timings && !is_platform_prebuilt {
|
||||
println!("Finished rebuilding the platform in {rebuild_duration} ms\n");
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(rebuild_thread)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (roc_app_bytes, code_gen_timing, expect_metadata) = gen_from_mono_module(
|
||||
arena,
|
||||
loaded,
|
||||
&app_module_path,
|
||||
target,
|
||||
code_gen_options,
|
||||
&preprocessed_host_path,
|
||||
&built_host_opt,
|
||||
wasm_dev_stack_bytes,
|
||||
);
|
||||
|
||||
|
@ -966,24 +969,18 @@ fn build_loaded_file<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(thread) = opt_rebuild_timing {
|
||||
let rebuild_duration = thread.join().expect("Failed to (re)build platform.");
|
||||
|
||||
if emit_timings && !is_platform_prebuilt {
|
||||
println!("Finished rebuilding the platform in {rebuild_duration} ms\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: link the prebuilt platform and compiled app
|
||||
// link the prebuilt platform and compiled app
|
||||
let link_start = Instant::now();
|
||||
|
||||
match (linking_strategy, link_type) {
|
||||
(LinkingStrategy::Surgical, _) => {
|
||||
let metadata_file = platform_main_roc_path.with_file_name(target.metadata_file_name());
|
||||
|
||||
roc_linker::link_preprocessed_host(
|
||||
target,
|
||||
&platform_main_roc,
|
||||
&roc_app_bytes,
|
||||
&output_exe_path,
|
||||
metadata_file,
|
||||
);
|
||||
}
|
||||
(LinkingStrategy::Additive, _) | (LinkingStrategy::Legacy, LinkType::None) => {
|
||||
|
@ -1008,36 +1005,57 @@ fn build_loaded_file<'a>(
|
|||
|
||||
std::fs::write(app_o_file, &*roc_app_bytes).unwrap();
|
||||
|
||||
let builtins_host_tempfile = roc_bitcode::host_tempfile()
|
||||
.expect("failed to write host builtins object to tempfile");
|
||||
|
||||
let mut inputs = vec![app_o_file.to_str().unwrap()];
|
||||
|
||||
if !matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||
// the host has been compiled into a .o or .obj file
|
||||
inputs.push(preprocessed_host_path.as_path().to_str().unwrap());
|
||||
let mut host_path = String::new();
|
||||
|
||||
match built_host_opt {
|
||||
BuiltHostOpt::Legacy(p) => {
|
||||
host_path.push_str(&p.to_string_lossy());
|
||||
inputs.push(&host_path);
|
||||
}
|
||||
BuiltHostOpt::None => {
|
||||
// In case of link_type == LinkType::Dylib or target == Target::Wasm32
|
||||
// When compiling a Dylib there is no host, such as when generating glue using `roc glue`.
|
||||
if target == Target::Wasm32 {
|
||||
let wasm_host_zig: PathBuf =
|
||||
platform_main_roc_path.with_file_name("host.zig");
|
||||
|
||||
assert!(
|
||||
wasm_host_zig.exists(),
|
||||
"No host.zig file found at {} when building wasm32 target.",
|
||||
wasm_host_zig.display()
|
||||
);
|
||||
|
||||
host_path.push_str(&wasm_host_zig.to_string_lossy());
|
||||
inputs.push(&host_path);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
panic!("Unexpected variant of built_host_opt in combination with `LinkingStrategy::Legacy`: {:?}", other);
|
||||
}
|
||||
}
|
||||
|
||||
let builtins_host_tempfile = roc_bitcode::host_tempfile()
|
||||
.expect("failed to write host builtins object to tempfile");
|
||||
|
||||
if matches!(code_gen_options.backend, CodeGenBackend::Assembly(_)) {
|
||||
inputs.push(builtins_host_tempfile.path().to_str().unwrap());
|
||||
}
|
||||
|
||||
let (mut child, _) = link(target, output_exe_path.clone(), &inputs, link_type)
|
||||
.map_err(|_| todo!("gracefully handle `ld` failing to spawn."))?;
|
||||
.map_err(|_| todo!("linker failed to spawn."))?;
|
||||
|
||||
let exit_status = child
|
||||
.wait()
|
||||
.map_err(|_| todo!("gracefully handle error after `ld` spawned"))?;
|
||||
.map_err(|_| todo!("linker error after spawning"))?;
|
||||
|
||||
// Extend the lifetime of the tempfile so it doesn't get dropped
|
||||
// (and thus deleted) before the child process is done using it!
|
||||
let _ = builtins_host_tempfile;
|
||||
|
||||
if !exit_status.success() {
|
||||
todo!(
|
||||
"gracefully handle `ld` (or `zig` in the case of wasm with --optimize) returning exit code {:?}",
|
||||
exit_status.code()
|
||||
);
|
||||
todo!("linker failed with exit code {:?}", exit_status.code());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1058,124 +1076,291 @@ fn build_loaded_file<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
fn invalid_prebuilt_platform(prebuilt_requested: bool, preprocessed_host_path: PathBuf) {
|
||||
let prefix = if prebuilt_requested {
|
||||
"Because I was run with --prebuilt-platform, "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
fn determine_built_host_path(
|
||||
platform_main_roc_path: &Path,
|
||||
target: Target,
|
||||
build_host_requested: bool,
|
||||
link_type: LinkType,
|
||||
linking_strategy: LinkingStrategy,
|
||||
suppress_build_host_warning: bool,
|
||||
) -> BuiltHostOpt {
|
||||
if build_host_requested {
|
||||
if !suppress_build_host_warning {
|
||||
// TODO
|
||||
//report_rebuilding_existing_host(&preprocessed_host.to_string_lossy());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
let preprocessed_host_path_str = preprocessed_host_path.to_string_lossy();
|
||||
let extra_err_msg = if preprocessed_host_path_str.ends_with(".rh") {
|
||||
"\n\n\tNote: If the platform does have an .rh1 file but no .rh file, it's because it's been built with an older version of roc. Contact the author to release a new build of the platform using a roc release newer than March 21 2023.\n"
|
||||
match link_type {
|
||||
LinkType::Executable => BuiltHostOpt::None,
|
||||
LinkType::Dylib => {
|
||||
eprintln!("You asked me to build the host, but I don't know how to rebuild a host for a dynamic library.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
LinkType::None => {
|
||||
eprintln!("You asked me to build the host, but I don't know how to rebuild a host for an unlinked object.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
""
|
||||
};
|
||||
match linking_strategy {
|
||||
LinkingStrategy::Legacy => {
|
||||
let legacy_host_path_res = target.find_legacy_host(platform_main_roc_path);
|
||||
|
||||
match legacy_host_path_res {
|
||||
Ok(legacy_host_path) => BuiltHostOpt::Legacy(legacy_host_path),
|
||||
Err(err_msg) => {
|
||||
eprintln!("Legacy linking failed: {}", err_msg);
|
||||
#[cfg(target_os = "linux")]
|
||||
eprintln!(
|
||||
"\n TIP: Maybe try surgical linking with the flag --linker=surgical"
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
LinkingStrategy::Surgical => {
|
||||
let surgical_artifacts = target.find_surgical_host(platform_main_roc_path);
|
||||
|
||||
match surgical_artifacts {
|
||||
Ok(surgical_artifacts) => BuiltHostOpt::Surgical(surgical_artifacts),
|
||||
Err(paths_str) => {
|
||||
// TODO improve error message
|
||||
eprintln!(
|
||||
"LinkingStrategy was set to Surgical (default), but \
|
||||
I tried to find the surgical host at any of these paths {} but it does not exist.",
|
||||
paths_str
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
LinkingStrategy::Additive => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get outut path for the executable.
|
||||
///
|
||||
/// If you specified a path that ends in in a directory separator, then
|
||||
/// use that directory, but use the app module's filename for the filename.
|
||||
fn get_exe_path(
|
||||
out_path: Option<&Path>,
|
||||
app_module_path: &Path,
|
||||
target: Target,
|
||||
linking_strategy: LinkingStrategy,
|
||||
link_type: LinkType,
|
||||
) -> PathBuf {
|
||||
match out_path {
|
||||
Some(path) => {
|
||||
// true iff the path ends with a directory separator,
|
||||
// e.g. '/' on UNIX, '/' or '\\' on Windows
|
||||
let ends_with_sep = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
path.as_os_str().as_bytes().ends_with(&[b'/'])
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
let last = path.as_os_str().encode_wide().last();
|
||||
|
||||
last == Some(0x002f)// UTF-16 slash
|
||||
|| last == Some(0x005c) // UTF-16 backslash
|
||||
}
|
||||
};
|
||||
|
||||
if ends_with_sep {
|
||||
let filename = app_module_path.file_name().unwrap_or_default();
|
||||
|
||||
with_output_extension(&path.join(filename), target, linking_strategy, link_type)
|
||||
} else {
|
||||
path.to_path_buf()
|
||||
}
|
||||
}
|
||||
None => with_output_extension(app_module_path, target, linking_strategy, link_type),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn report_rebuilding_existing_host(host_path: &str) {
|
||||
eprintln!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
{}I was expecting this file to exist:
|
||||
WARNING: I found an existing compiled host at:
|
||||
|
||||
{}
|
||||
|
||||
However, it was not there!{}
|
||||
However, the --build-host flag was set! I will rebuild the host and overwrite the existing file.
|
||||
|
||||
If you have the platform's source code locally, you may be able to generate it by re-running this command omitting --prebuilt-platform
|
||||
Remove the --build-host flag to use the existing host and silence this warning.
|
||||
Rebuilding hosts using the roc compiler is deprecated and will be removed in a future version.
|
||||
"#
|
||||
),
|
||||
prefix,
|
||||
preprocessed_host_path.to_string_lossy(),
|
||||
extra_err_msg
|
||||
host_path,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn spawn_rebuild_thread(
|
||||
#[allow(dead_code)]
|
||||
fn report_rebuilding_missing_host(host_path: &str) {
|
||||
eprintln!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
WARNING: I was expecting a prebuilt host to exist at:
|
||||
|
||||
{}
|
||||
|
||||
However, it was not there! I will rebuild the host and write it to that location.
|
||||
|
||||
Rebuilding hosts using the roc compiler is deprecated and will be removed in a future version.
|
||||
"#
|
||||
),
|
||||
host_path,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn report_missing_prebuilt_host(msg: &str) {
|
||||
eprintln!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
I was expecting a prebuilt host to exist:
|
||||
|
||||
{}
|
||||
|
||||
However, it was not there!
|
||||
|
||||
If you have the platform's source code locally, you may be able to generate it by using a build script.
|
||||
"#
|
||||
),
|
||||
msg
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn report_refusing_to_rebuild_host(host_path: &str) {
|
||||
eprintln!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
I found a prebuilt host for this platform, but you requested to rebuild it:
|
||||
|
||||
{}
|
||||
|
||||
Remove the `--build-host` flag to use the prebuilt host.
|
||||
The `--build-host` flag is deprecated and will be removed in a future release.
|
||||
"#
|
||||
),
|
||||
host_path,
|
||||
);
|
||||
}
|
||||
|
||||
fn spawn_wasm32_host_build_thread(
|
||||
opt_level: OptLevel,
|
||||
linking_strategy: LinkingStrategy,
|
||||
platform_main_roc: PathBuf,
|
||||
preprocessed_host_path: PathBuf,
|
||||
output_exe_path: PathBuf,
|
||||
target: Target,
|
||||
dll_stub_symbols: Vec<String>,
|
||||
) -> std::thread::JoinHandle<u128> {
|
||||
platform_main_roc: PathBuf,
|
||||
output_path: PathBuf,
|
||||
) -> std::thread::JoinHandle<(u128, BuiltHostOpt)> {
|
||||
std::thread::spawn(move || {
|
||||
// Printing to stderr because we want stdout to contain only the output of the roc program.
|
||||
// We are aware of the trade-offs.
|
||||
// `cargo run` follows the same approach
|
||||
eprintln!("🔨 Rebuilding platform...");
|
||||
eprintln!("🔨 Building host ...");
|
||||
|
||||
let rebuild_host_start = Instant::now();
|
||||
let start = Instant::now();
|
||||
|
||||
match linking_strategy {
|
||||
LinkingStrategy::Additive => {
|
||||
let host_dest = rebuild_host(opt_level, target, platform_main_roc.as_path(), None);
|
||||
let host_dest = rebuild_host(opt_level, target, platform_main_roc.as_path(), None);
|
||||
|
||||
preprocess_host_wasm32(host_dest.as_path(), &preprocessed_host_path);
|
||||
}
|
||||
LinkingStrategy::Surgical => {
|
||||
build_and_preprocess_host_lowlevel(
|
||||
opt_level,
|
||||
target,
|
||||
platform_main_roc.as_path(),
|
||||
preprocessed_host_path.as_path(),
|
||||
&dll_stub_symbols,
|
||||
);
|
||||
preprocess_host_wasm32(host_dest.as_path(), &output_path);
|
||||
|
||||
// Copy preprocessed host to executable location.
|
||||
// The surgical linker will modify that copy in-place.
|
||||
std::fs::copy(&preprocessed_host_path, output_exe_path.as_path()).unwrap();
|
||||
}
|
||||
LinkingStrategy::Legacy => {
|
||||
rebuild_host(opt_level, target, platform_main_roc.as_path(), None);
|
||||
}
|
||||
}
|
||||
|
||||
rebuild_host_start.elapsed().as_millis()
|
||||
(
|
||||
start.elapsed().as_millis(),
|
||||
BuiltHostOpt::Additive(output_path),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_and_preprocess_host(
|
||||
/// Note this will copy the preprocessed host to the executable location
|
||||
/// where the surgical linker will modify that copy in-place.
|
||||
fn spawn_surgical_host_build_thread(
|
||||
opt_level: OptLevel,
|
||||
target: Target,
|
||||
platform_main_roc: &Path,
|
||||
preprocessed_host_path: &Path,
|
||||
exposed_symbols: roc_linker::ExposedSymbols,
|
||||
) {
|
||||
let stub_dll_symbols = exposed_symbols.stub_dll_symbols();
|
||||
platform_main_roc: PathBuf,
|
||||
dll_stub_symbols: Vec<String>,
|
||||
preprocessed_path: PathBuf,
|
||||
output_exe_path: PathBuf,
|
||||
metadata_path: PathBuf,
|
||||
) -> std::thread::JoinHandle<(u128, BuiltHostOpt)> {
|
||||
std::thread::spawn(move || {
|
||||
// Printing to stderr because we want stdout to contain only the output of the roc program.
|
||||
// We are aware of the trade-offs.
|
||||
// `cargo run` follows the same approach
|
||||
eprintln!("🔨 Building host ...");
|
||||
|
||||
build_and_preprocess_host_lowlevel(
|
||||
opt_level,
|
||||
target,
|
||||
platform_main_roc,
|
||||
preprocessed_host_path,
|
||||
&stub_dll_symbols,
|
||||
)
|
||||
let start = Instant::now();
|
||||
|
||||
let stub_lib = roc_linker::generate_stub_lib_from_loaded(
|
||||
target,
|
||||
platform_main_roc.as_path(),
|
||||
dll_stub_symbols.as_slice(),
|
||||
);
|
||||
|
||||
debug_assert!(stub_lib.exists());
|
||||
|
||||
let host_exe = rebuild_host(
|
||||
opt_level,
|
||||
target,
|
||||
platform_main_roc.as_path(),
|
||||
Some(&stub_lib),
|
||||
);
|
||||
|
||||
roc_linker::preprocess_host(
|
||||
target,
|
||||
host_exe.as_path(),
|
||||
metadata_path.as_path(),
|
||||
preprocessed_path.as_path(),
|
||||
&stub_lib,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
// Copy preprocessed host to executable location.
|
||||
// The surgical linker will modify that copy in-place.
|
||||
std::fs::copy(&preprocessed_path, &output_exe_path).unwrap();
|
||||
|
||||
(
|
||||
start.elapsed().as_millis(),
|
||||
BuiltHostOpt::Surgical(SurgicalHostArtifacts {
|
||||
metadata: metadata_path,
|
||||
preprocessed_host: preprocessed_path,
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn build_and_preprocess_host_lowlevel(
|
||||
// Note the output host will be
|
||||
fn spawn_legacy_host_build_thread(
|
||||
opt_level: OptLevel,
|
||||
target: Target,
|
||||
platform_main_roc: &Path,
|
||||
_preprocessed_host_path: &Path,
|
||||
stub_dll_symbols: &[String],
|
||||
) {
|
||||
let stub_lib =
|
||||
roc_linker::generate_stub_lib_from_loaded(target, platform_main_roc, stub_dll_symbols);
|
||||
platform_main_roc: PathBuf,
|
||||
) -> std::thread::JoinHandle<(u128, BuiltHostOpt)> {
|
||||
std::thread::spawn(move || {
|
||||
// Printing to stderr because we want stdout to contain only the output of the roc program.
|
||||
// We are aware of the trade-offs.
|
||||
// `cargo run` follows the same approach
|
||||
eprintln!("🔨 Building host ...");
|
||||
|
||||
debug_assert!(stub_lib.exists());
|
||||
let start = Instant::now();
|
||||
|
||||
let host_dest = rebuild_host(opt_level, target, platform_main_roc, Some(&stub_lib));
|
||||
let host_dest = rebuild_host(opt_level, target, platform_main_roc.as_path(), None);
|
||||
|
||||
roc_linker::preprocess_host(
|
||||
target,
|
||||
host_dest.as_path(),
|
||||
platform_main_roc,
|
||||
&stub_lib,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
(start.elapsed().as_millis(), BuiltHostOpt::Legacy(host_dest))
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -1261,7 +1446,7 @@ pub fn build_str_test<'a>(
|
|||
arena: &'a Bump,
|
||||
app_module_path: &Path,
|
||||
app_module_source: &'a str,
|
||||
assume_prebuild: bool,
|
||||
build_host_requested: bool,
|
||||
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
|
||||
let target = target_lexicon::Triple::host().into();
|
||||
|
||||
|
@ -1298,6 +1483,9 @@ pub fn build_str_test<'a>(
|
|||
)
|
||||
.map_err(|e| BuildFileError::from_mono_error(e, compilation_start))?;
|
||||
|
||||
// we are in a test, so we don't need to provide a warning about rebuilding the host
|
||||
let suppress_build_host_warning = true;
|
||||
|
||||
build_loaded_file(
|
||||
arena,
|
||||
target,
|
||||
|
@ -1306,7 +1494,8 @@ pub fn build_str_test<'a>(
|
|||
emit_timings,
|
||||
link_type,
|
||||
linking_strategy,
|
||||
assume_prebuild,
|
||||
build_host_requested,
|
||||
suppress_build_host_warning,
|
||||
wasm_dev_stack_bytes,
|
||||
loaded,
|
||||
compilation_start,
|
||||
|
|
|
@ -5406,7 +5406,7 @@ mod test_reporting {
|
|||
2 -> 2
|
||||
"
|
||||
),
|
||||
@r#"
|
||||
@r###"
|
||||
── UNKNOWN OPERATOR in tmp/when_outdented_branch/Test.roc ──────────────────────
|
||||
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
|
@ -5424,7 +5424,7 @@ mod test_reporting {
|
|||
In Roc, functions are always written as a lambda, like
|
||||
|
||||
increment = \n -> n + 1
|
||||
"#
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -5635,7 +5635,7 @@ mod test_reporting {
|
|||
test_report!(
|
||||
return_space_problem,
|
||||
"return \t",
|
||||
@r"
|
||||
@r###"
|
||||
── TAB CHARACTER in tmp/return_space_problem/Test.roc ──────────────────────────
|
||||
|
||||
I encountered a tab character:
|
||||
|
@ -5644,7 +5644,7 @@ mod test_reporting {
|
|||
^
|
||||
|
||||
Tab characters are not allowed in Roc code. Please use spaces instead!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -6253,7 +6253,7 @@ All branches in an `if` must have the same type!
|
|||
r#"
|
||||
app "broken"
|
||||
packages {
|
||||
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br",
|
||||
pf: "generic-test-platform/main.roc",
|
||||
}
|
||||
imports [
|
||||
pf.Stdout,
|
||||
|
@ -14659,7 +14659,7 @@ All branches in an `if` must have the same type!
|
|||
leftover_statement,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14690,7 +14690,7 @@ All branches in an `if` must have the same type!
|
|||
fx_fn_annotated_as_pure,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14728,7 +14728,7 @@ All branches in an `if` must have the same type!
|
|||
fx_fn_annotated_as_pure_stmt,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14776,7 +14776,7 @@ All branches in an `if` must have the same type!
|
|||
nested_function_def_fx_no_bang,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14807,7 +14807,7 @@ All branches in an `if` must have the same type!
|
|||
ignored_result_stmt,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14839,7 +14839,7 @@ All branches in an `if` must have the same type!
|
|||
ignored_stmt_forgot_to_call,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14881,7 +14881,7 @@ All branches in an `if` must have the same type!
|
|||
function_def_leftover_bang,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14910,7 +14910,7 @@ All branches in an `if` must have the same type!
|
|||
effect_in_top_level_value_def,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14948,7 +14948,7 @@ All branches in an `if` must have the same type!
|
|||
aliased_fx_fn,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -14978,7 +14978,7 @@ All branches in an `if` must have the same type!
|
|||
unsuffixed_fx_in_record,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15010,7 +15010,7 @@ All branches in an `if` must have the same type!
|
|||
suffixed_pure_in_record,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15040,7 +15040,7 @@ All branches in an `if` must have the same type!
|
|||
unsuffixed_fx_arg,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15077,7 +15077,7 @@ All branches in an `if` must have the same type!
|
|||
suffixed_pure_arg,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15112,7 +15112,7 @@ All branches in an `if` must have the same type!
|
|||
unsuffixed_tuple_fx_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15156,7 +15156,7 @@ All branches in an `if` must have the same type!
|
|||
suffixed_tuple_pure_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15184,7 +15184,7 @@ All branches in an `if` must have the same type!
|
|||
unsuffixed_tag_fx_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15228,7 +15228,7 @@ All branches in an `if` must have the same type!
|
|||
suffixed_tag_pure_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15256,7 +15256,7 @@ All branches in an `if` must have the same type!
|
|||
unsuffixed_opaque_fx_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15288,7 +15288,7 @@ All branches in an `if` must have the same type!
|
|||
suffixed_opaque_pure_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15318,7 +15318,7 @@ All branches in an `if` must have the same type!
|
|||
fx_passed_to_untyped_pure_hof,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
@ -15361,7 +15361,7 @@ All branches in an `if` must have the same type!
|
|||
fx_passed_to_partially_inferred_pure_hof,
|
||||
indoc!(
|
||||
r#"
|
||||
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||
app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
|
|
|
@ -1462,6 +1462,8 @@ pub fn load<'a>(
|
|||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||
enum Threads {
|
||||
Single,
|
||||
|
||||
#[allow(dead_code)]
|
||||
Many(usize),
|
||||
}
|
||||
|
||||
|
@ -3250,7 +3252,7 @@ fn finish_specialization<'a>(
|
|||
.collect();
|
||||
|
||||
let module_id = state.root_id;
|
||||
let uses_prebuilt_platform = match platform_data {
|
||||
let needs_prebuilt_host = match platform_data {
|
||||
Some(data) => data.is_prebuilt,
|
||||
// If there's no platform data (e.g. because we're building a module)
|
||||
// then there's no prebuilt platform either!
|
||||
|
@ -3273,7 +3275,7 @@ fn finish_specialization<'a>(
|
|||
timings: state.timings,
|
||||
toplevel_expects,
|
||||
glue_layouts: GlueLayouts { getters: vec![] },
|
||||
uses_prebuilt_platform,
|
||||
needs_prebuilt_host,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ pub struct MonomorphizedModule<'a> {
|
|||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||
pub expectations: VecMap<ModuleId, Expectations>,
|
||||
pub uses_prebuilt_platform: bool,
|
||||
pub needs_prebuilt_host: bool,
|
||||
pub glue_layouts: GlueLayouts<'a>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1137,15 +1137,15 @@ fn explicit_builtin_import() {
|
|||
indoc!(
|
||||
r"
|
||||
── EXPLICIT BUILTIN IMPORT in tmp/explicit_builtin_import/Main.roc ─────────────
|
||||
|
||||
|
||||
The builtin Bool was imported here:
|
||||
|
||||
|
||||
3│ import Bool
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
||||
Builtins are imported automatically, so you can remove this import.
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
<https://www.roc-lang.org/tutorial#builtin-modules>
|
||||
"
|
||||
)
|
||||
|
@ -1172,15 +1172,15 @@ fn explicit_builtin_import_empty_exposing() {
|
|||
indoc!(
|
||||
r"
|
||||
── EXPLICIT BUILTIN IMPORT in tmp/empty_exposing_builtin_import/Main.roc ───────
|
||||
|
||||
|
||||
The builtin Bool was imported here:
|
||||
|
||||
|
||||
3│ import Bool exposing []
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
Builtins are imported automatically, so you can remove this import.
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
<https://www.roc-lang.org/tutorial#builtin-modules>
|
||||
"
|
||||
)
|
||||
|
@ -1211,16 +1211,16 @@ fn explicit_builtin_type_import() {
|
|||
indoc!(
|
||||
r"
|
||||
── EXPLICIT BUILTIN IMPORT in tmp/explicit_builtin_type_import/Main.roc ────────
|
||||
|
||||
|
||||
`Dict.Dict` was imported here:
|
||||
|
||||
|
||||
3│ import Dict exposing [Dict, isEmpty]
|
||||
^^^^
|
||||
|
||||
|
||||
All types from builtins are automatically exposed, so you can remove
|
||||
`Dict` from the exposing list.
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
|
||||
Tip: Learn more about builtins in the tutorial:
|
||||
<https://www.roc-lang.org/tutorial#builtin-modules>
|
||||
"
|
||||
)
|
||||
|
@ -2043,7 +2043,7 @@ fn module_cyclic_import_transitive() {
|
|||
indoc!(
|
||||
r"
|
||||
module []
|
||||
|
||||
|
||||
import Age
|
||||
"
|
||||
),
|
||||
|
@ -2108,7 +2108,7 @@ fn roc_file_no_extension() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "helloWorld"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br" }
|
||||
packages { pf: "generic-test-platform/main.roc" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
|
|
@ -1790,7 +1790,7 @@ impl<'a> LambdaSet<'a> {
|
|||
// functions, despite not appearing in the lambda set.
|
||||
// We don't want to compile them as thunks, so we need to figure out a special-casing for
|
||||
// them.
|
||||
// To reproduce: test cli_run
|
||||
// To reproduce: test cli_tests
|
||||
//
|
||||
// debug_assert!(
|
||||
// self.set
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
|
|||
path.push("examples");
|
||||
path.push("cli");
|
||||
path.push("false-interpreter");
|
||||
path.push("False.roc");
|
||||
path.push("main.roc");
|
||||
let src = std::fs::read_to_string(&path).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use roc_error_macros::user_error;
|
||||
|
@ -17,6 +18,18 @@ pub enum OperatingSystem {
|
|||
Windows,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for OperatingSystem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let arch_str = match self {
|
||||
OperatingSystem::Freestanding => "freestanding",
|
||||
OperatingSystem::Linux => "linux",
|
||||
OperatingSystem::Mac => "macos",
|
||||
OperatingSystem::Windows => "windows",
|
||||
};
|
||||
write!(f, "{}", arch_str)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PtrWidth {
|
||||
|
@ -36,8 +49,8 @@ pub enum Architecture {
|
|||
impl std::fmt::Display for Architecture {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let arch_str = match self {
|
||||
Architecture::Aarch32 => "aarch32",
|
||||
Architecture::Aarch64 => "aarch64",
|
||||
Architecture::Aarch32 => "arm",
|
||||
Architecture::Aarch64 => "arm64",
|
||||
Architecture::Wasm32 => "wasm32",
|
||||
Architecture::X86_32 => "x86_32",
|
||||
Architecture::X86_64 => "x86_64",
|
||||
|
@ -74,6 +87,12 @@ pub enum Target {
|
|||
Wasm32,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SurgicalHostArtifacts {
|
||||
pub metadata: PathBuf,
|
||||
pub preprocessed_host: PathBuf,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub const fn architecture(&self) -> Architecture {
|
||||
use Target::*;
|
||||
|
@ -122,6 +141,7 @@ impl Target {
|
|||
self.architecture().ptr_alignment_bytes()
|
||||
}
|
||||
|
||||
// file extension for an object file
|
||||
pub const fn object_file_ext(&self) -> &str {
|
||||
use Target::*;
|
||||
match self {
|
||||
|
@ -131,6 +151,7 @@ impl Target {
|
|||
}
|
||||
}
|
||||
|
||||
// file extension for a static library file
|
||||
pub const fn static_library_file_ext(&self) -> &str {
|
||||
use Target::*;
|
||||
match self {
|
||||
|
@ -140,6 +161,18 @@ impl Target {
|
|||
}
|
||||
}
|
||||
|
||||
// file extension for a dynamic/shared library file
|
||||
pub const fn dynamic_library_file_ext(&self) -> &str {
|
||||
use Target::*;
|
||||
match self {
|
||||
LinuxX32 | LinuxX64 | LinuxArm64 => "so",
|
||||
MacX64 | MacArm64 => "dylib",
|
||||
WinX32 | WinX64 | WinArm64 => "dll",
|
||||
Wasm32 => "wasm",
|
||||
}
|
||||
}
|
||||
|
||||
// file extension for an executable file
|
||||
pub const fn executable_file_ext(&self) -> Option<&str> {
|
||||
use Target::*;
|
||||
match self {
|
||||
|
@ -148,6 +181,137 @@ impl Target {
|
|||
Wasm32 => Some("wasm"),
|
||||
}
|
||||
}
|
||||
|
||||
// file name for a prebuilt host object file
|
||||
// used for legacy linking
|
||||
pub fn prebuilt_static_object(&self) -> String {
|
||||
use Target::*;
|
||||
match self {
|
||||
LinuxX32 | LinuxX64 | LinuxArm64 | MacX64 | MacArm64 | Wasm32 => {
|
||||
format!("{}.o", self)
|
||||
}
|
||||
WinX32 | WinX64 | WinArm64 => {
|
||||
format!("{}.obj", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// file name for a prebuilt host static library file
|
||||
// used for legacy linking
|
||||
pub fn prebuilt_static_library(&self) -> String {
|
||||
use Target::*;
|
||||
match self {
|
||||
LinuxX32 | LinuxX64 | LinuxArm64 | MacX64 | MacArm64 | Wasm32 => {
|
||||
format!("{}.a", self)
|
||||
}
|
||||
WinX32 | WinX64 | WinArm64 => {
|
||||
format!("{}.lib", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// file name for a preprocessed host executable file
|
||||
// used for surgical linking
|
||||
pub fn prebuilt_surgical_host(&self) -> String {
|
||||
format!("{}.rh", self) // short for roc host
|
||||
}
|
||||
|
||||
// file name for a preprocessed host metadata file
|
||||
// used for surgical linking
|
||||
pub fn metadata_file_name(&self) -> String {
|
||||
format!("metadata_{}.rm", self) // short for roc metadata
|
||||
}
|
||||
|
||||
// file name for a stubbed app dynamic library file
|
||||
pub fn stub_app_lib_file_name(&self) -> String {
|
||||
format!("libapp.{}", self.dynamic_library_file_ext())
|
||||
}
|
||||
|
||||
/// Search for a prebuilt legacy host in the platform main directory.
|
||||
pub fn find_legacy_host(&self, platform_main_roc: &Path) -> Result<PathBuf, String> {
|
||||
let static_library_path = platform_main_roc.with_file_name(self.prebuilt_static_library());
|
||||
|
||||
let static_object_path = platform_main_roc.with_file_name(self.prebuilt_static_object());
|
||||
|
||||
let generic_host_path: PathBuf = platform_main_roc
|
||||
.with_file_name("libhost")
|
||||
.with_extension(self.static_library_file_ext());
|
||||
|
||||
if static_library_path.exists() {
|
||||
Ok(static_library_path)
|
||||
} else if generic_host_path.exists() {
|
||||
Ok(generic_host_path)
|
||||
} else if static_object_path.exists() {
|
||||
Ok(static_object_path)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Failed to find any legacy linking files; I need one of these three paths to exist:\n {}\n {}\n {}",
|
||||
static_library_path.display(),
|
||||
static_object_path.display(),
|
||||
generic_host_path.display(),
|
||||
)
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Search for a prebuilt surgical host in the platform main directory.
|
||||
pub fn find_surgical_host(
|
||||
&self,
|
||||
platform_main_roc: &Path,
|
||||
) -> Result<SurgicalHostArtifacts, String> {
|
||||
let surgical_metadata = platform_main_roc.with_file_name(self.metadata_file_name());
|
||||
let surgical_host_path = platform_main_roc.with_file_name(self.prebuilt_surgical_host());
|
||||
|
||||
let generic_host_path: PathBuf = platform_main_roc.with_file_name("host.rh");
|
||||
let generic_metadata: PathBuf = platform_main_roc.with_file_name("metadata_host.rm");
|
||||
|
||||
if generic_host_path.exists() && generic_metadata.exists() {
|
||||
Ok(SurgicalHostArtifacts {
|
||||
metadata: generic_metadata,
|
||||
preprocessed_host: generic_host_path,
|
||||
})
|
||||
} else if surgical_host_path.exists() && surgical_metadata.exists() {
|
||||
Ok(SurgicalHostArtifacts {
|
||||
metadata: surgical_metadata,
|
||||
preprocessed_host: surgical_host_path,
|
||||
})
|
||||
} else {
|
||||
// TODO further improve the error message
|
||||
|
||||
Err(format!(
|
||||
"Either the generic host files or the surgical host files must exist. \
|
||||
File status: \
|
||||
Generic host ({}): {}, \
|
||||
Generic metadata ({}): {}, \
|
||||
Surgical host ({}): {}, \
|
||||
Surgical metadata ({}): {}",
|
||||
generic_host_path.display(),
|
||||
if generic_host_path.exists() {
|
||||
"present"
|
||||
} else {
|
||||
"missing"
|
||||
},
|
||||
generic_metadata.display(),
|
||||
if generic_metadata.exists() {
|
||||
"present"
|
||||
} else {
|
||||
"missing"
|
||||
},
|
||||
surgical_host_path.display(),
|
||||
if surgical_host_path.exists() {
|
||||
"present"
|
||||
} else {
|
||||
"missing"
|
||||
},
|
||||
surgical_metadata.display(),
|
||||
if surgical_metadata.exists() {
|
||||
"present"
|
||||
} else {
|
||||
"missing"
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ParseError {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
app [main] {
|
||||
pf:
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br",
|
||||
}
|
||||
|
||||
main =
|
||||
Stdout.line "I'm a Roc application!"
|
|
@ -11,22 +11,15 @@ Full(
|
|||
),
|
||||
],
|
||||
before_packages: [],
|
||||
packages: @11-134 [
|
||||
@13-132 SpaceAfter(
|
||||
PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [
|
||||
Newline,
|
||||
],
|
||||
platform_marker: None,
|
||||
package_name: @17-132 PackageName(
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br",
|
||||
),
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
packages: @11-51 [
|
||||
@13-49 PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [],
|
||||
platform_marker: None,
|
||||
package_name: @17-49 PackageName(
|
||||
"generic-test-platform/main.roc",
|
||||
),
|
||||
},
|
||||
],
|
||||
old_imports: None,
|
||||
old_provides_to_new_package: None,
|
||||
|
@ -38,7 +31,7 @@ Full(
|
|||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@136-183,
|
||||
@53-100,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 2 },
|
||||
|
@ -54,17 +47,17 @@ Full(
|
|||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@136-140 Identifier {
|
||||
@53-57 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@147-183 SpaceBefore(
|
||||
@64-100 SpaceBefore(
|
||||
Apply(
|
||||
@147-158 Var {
|
||||
@64-75 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@159-183 Str(
|
||||
@76-100 Str(
|
||||
PlainLine(
|
||||
"I'm a Roc application!",
|
||||
),
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
app [main] { pf:
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br"
|
||||
}
|
||||
app [main] { pf: "generic-test-platform/main.roc" }
|
||||
|
||||
main =
|
||||
Stdout.line "I'm a Roc application!"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue