mirror of
https://github.com/roc-lang/roc.git
synced 2024-08-16 22:30:58 +03:00
Add --output to build
, default to app filename
This commit is contained in:
parent
a96752a65a
commit
eebec8a378
@ -150,6 +150,12 @@ pub fn build_app() -> Command {
|
||||
.args_conflicts_with_subcommands(true)
|
||||
.subcommand(Command::new(CMD_BUILD)
|
||||
.about("Build a binary from the given .roc file, but don't run it")
|
||||
.arg(Arg::new(FLAG_OUTPUT)
|
||||
.long(FLAG_OUTPUT)
|
||||
.help("The full path to the output binary, including filename. To specify directory only, specify a path that ends in a directory separator (e.g. a slash).")
|
||||
.value_parser(value_parser!(OsString))
|
||||
.required(false)
|
||||
)
|
||||
.arg(flag_optimize.clone())
|
||||
.arg(flag_max_threads.clone())
|
||||
.arg(flag_opt_size.clone())
|
||||
@ -537,6 +543,7 @@ pub fn build(
|
||||
subcommands: &[String],
|
||||
config: BuildConfig,
|
||||
triple: Triple,
|
||||
out_path: Option<&Path>,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
link_type: LinkType,
|
||||
) -> io::Result<i32> {
|
||||
@ -725,6 +732,7 @@ pub fn build(
|
||||
wasm_dev_stack_bytes,
|
||||
roc_cache_dir,
|
||||
load_config,
|
||||
out_path,
|
||||
);
|
||||
|
||||
match res_binary_path {
|
||||
|
@ -48,6 +48,7 @@ fn main() -> io::Result<()> {
|
||||
&subcommands,
|
||||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
@ -62,6 +63,7 @@ fn main() -> io::Result<()> {
|
||||
&subcommands,
|
||||
BuildConfig::BuildAndRun,
|
||||
Triple::host(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
@ -87,6 +89,7 @@ fn main() -> io::Result<()> {
|
||||
&subcommands,
|
||||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
@ -140,12 +143,16 @@ fn main() -> io::Result<()> {
|
||||
(false, true) => LinkType::None,
|
||||
(false, false) => LinkType::Executable,
|
||||
};
|
||||
let out_path = matches
|
||||
.get_one::<OsString>(FLAG_OUTPUT)
|
||||
.map(OsString::as_ref);
|
||||
|
||||
Ok(build(
|
||||
matches,
|
||||
&subcommands,
|
||||
BuildConfig::BuildOnly,
|
||||
target.to_triple(),
|
||||
out_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
link_type,
|
||||
)?)
|
||||
|
@ -17,7 +17,7 @@ use roc_reporting::{
|
||||
cli::{report_problems, Problems},
|
||||
report::{RenderTarget, DEFAULT_PALETTE},
|
||||
};
|
||||
use roc_target::TargetInfo;
|
||||
use roc_target::{OperatingSystem, TargetInfo};
|
||||
use std::ffi::OsStr;
|
||||
use std::ops::Deref;
|
||||
use std::{
|
||||
@ -773,6 +773,7 @@ pub fn build_file<'a>(
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
load_config: LoadConfig,
|
||||
out_path: Option<&Path>,
|
||||
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
|
||||
let compilation_start = Instant::now();
|
||||
|
||||
@ -793,6 +794,7 @@ pub fn build_file<'a>(
|
||||
wasm_dev_stack_bytes,
|
||||
loaded,
|
||||
compilation_start,
|
||||
out_path,
|
||||
)
|
||||
}
|
||||
|
||||
@ -809,6 +811,7 @@ fn build_loaded_file<'a>(
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
loaded: roc_load::MonomorphizedModule<'a>,
|
||||
compilation_start: Instant,
|
||||
out_path: Option<&Path>,
|
||||
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
|
||||
let operating_system = roc_target::OperatingSystem::from(target.operating_system);
|
||||
|
||||
@ -834,12 +837,40 @@ fn build_loaded_file<'a>(
|
||||
// even if the --prebuilt-platform CLI flag wasn't set.
|
||||
let is_platform_prebuilt = prebuilt_requested || loaded.uses_prebuilt_platform;
|
||||
|
||||
let cwd = app_module_path.parent().unwrap();
|
||||
let mut output_exe_path = cwd.join(&*loaded.output_path);
|
||||
let mut 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;
|
||||
|
||||
if let Some(extension) = operating_system.executable_file_ext() {
|
||||
output_exe_path.set_extension(extension);
|
||||
}
|
||||
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('/') || last == Some('\\');
|
||||
}
|
||||
};
|
||||
|
||||
// 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_executable_extension(&path.join(filename), operating_system)
|
||||
} else {
|
||||
path.to_path_buf()
|
||||
}
|
||||
}
|
||||
None => with_executable_extension(&app_module_path, operating_system),
|
||||
};
|
||||
|
||||
// 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) {
|
||||
@ -1322,5 +1353,10 @@ pub fn build_str_test<'a>(
|
||||
wasm_dev_stack_bytes,
|
||||
loaded,
|
||||
compilation_start,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn with_executable_extension(path: &Path, os: OperatingSystem) -> PathBuf {
|
||||
path.with_extension(os.executable_file_ext().unwrap_or_default())
|
||||
}
|
||||
|
@ -69,7 +69,6 @@ use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Var
|
||||
use roc_types::types::{Alias, Types};
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::collections::HashMap;
|
||||
use std::env::current_dir;
|
||||
use std::io;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
@ -91,9 +90,6 @@ use crate::wasm_instant::{Duration, Instant};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Default name for the binary generated for an app, if an invalid one was specified.
|
||||
const DEFAULT_APP_OUTPUT_PATH: &str = "app";
|
||||
|
||||
/// Filename extension for normal Roc modules
|
||||
const ROC_FILE_EXTENSION: &str = "roc";
|
||||
|
||||
@ -697,7 +693,6 @@ struct State<'a> {
|
||||
pub opt_platform_shorthand: Option<&'a str>,
|
||||
pub platform_data: Option<PlatformData<'a>>,
|
||||
pub exposed_types: ExposedByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: PlatformPath<'a>,
|
||||
pub target_info: TargetInfo,
|
||||
pub(self) function_kind: FunctionKind,
|
||||
@ -785,7 +780,6 @@ impl<'a> State<'a> {
|
||||
target_info,
|
||||
function_kind,
|
||||
platform_data: None,
|
||||
output_path: None,
|
||||
platform_path: PlatformPath::NotSpecified,
|
||||
module_cache: ModuleCache::default(),
|
||||
dependencies,
|
||||
@ -2286,7 +2280,6 @@ fn update<'a>(
|
||||
|
||||
match header.header_type {
|
||||
App { to_platform, .. } => {
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::Valid(to_platform);
|
||||
}
|
||||
Package {
|
||||
@ -2469,24 +2462,9 @@ fn update<'a>(
|
||||
.sources
|
||||
.insert(parsed.module_id, (parsed.module_path.clone(), parsed.src));
|
||||
|
||||
// If this was an app module, set the output path to be
|
||||
// the module's declared "name".
|
||||
//
|
||||
// e.g. for `app "blah"` we should generate an output file named "blah"
|
||||
if let HeaderType::App { output_name, .. } = &parsed.header_type {
|
||||
match output_name {
|
||||
StrLiteral::PlainLine(path) => {
|
||||
state.output_path = Some(path);
|
||||
}
|
||||
_ => {
|
||||
todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let module_id = parsed.module_id;
|
||||
|
||||
state.module_cache.parsed.insert(parsed.module_id, parsed);
|
||||
state.module_cache.parsed.insert(module_id, parsed);
|
||||
|
||||
let work = state.dependencies.notify(module_id, Phase::Parse);
|
||||
|
||||
@ -3229,7 +3207,6 @@ fn finish_specialization<'a>(
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
module_cache,
|
||||
output_path,
|
||||
platform_data,
|
||||
..
|
||||
} = state;
|
||||
@ -3247,12 +3224,6 @@ fn finish_specialization<'a>(
|
||||
.collect();
|
||||
|
||||
let module_id = state.root_id;
|
||||
|
||||
let output_path = match output_path {
|
||||
Some(path_str) => Path::new(path_str).into(),
|
||||
None => current_dir().unwrap().join(DEFAULT_APP_OUTPUT_PATH).into(),
|
||||
};
|
||||
|
||||
let uses_prebuilt_platform = match platform_data {
|
||||
Some(data) => data.is_prebuilt,
|
||||
// If there's no platform data (e.g. because we're building an interface module)
|
||||
@ -3263,7 +3234,6 @@ fn finish_specialization<'a>(
|
||||
Ok(MonomorphizedModule {
|
||||
can_problems,
|
||||
type_problems,
|
||||
output_path,
|
||||
expectations: module_expectations,
|
||||
exposed_to_host,
|
||||
module_id,
|
||||
|
@ -20,7 +20,6 @@ use roc_solve::module::Solved;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Types};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
@ -163,7 +162,6 @@ pub struct MonomorphizedModule<'a> {
|
||||
pub interns: Interns,
|
||||
pub subs: Subs,
|
||||
pub layout_interner: STLayoutInterner<'a>,
|
||||
pub output_path: Box<Path>,
|
||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
|
@ -13,8 +13,8 @@ roc_builtins = { path = "../compiler/builtins" }
|
||||
roc_can = { path = "../compiler/can" }
|
||||
roc_collections = { path = "../compiler/collections" }
|
||||
roc_error_macros = { path = "../error_macros" }
|
||||
roc_gen_llvm= { path = "../compiler/gen_llvm" }
|
||||
roc_linker = { path = "../linker"}
|
||||
roc_gen_llvm = { path = "../compiler/gen_llvm" }
|
||||
roc_linker = { path = "../linker" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
roc_module = { path = "../compiler/module" }
|
||||
roc_mono = { path = "../compiler/mono" }
|
||||
@ -34,6 +34,7 @@ libloading.workspace = true
|
||||
strum.workspace = true
|
||||
strum_macros.workspace = true
|
||||
target-lexicon.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
@ -41,4 +42,3 @@ dircpy.workspace = true
|
||||
|
||||
indoc.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
@ -71,21 +71,30 @@ pub fn generate(
|
||||
LinkingStrategy::Legacy
|
||||
};
|
||||
|
||||
let res_binary_path = build_file(
|
||||
&arena,
|
||||
&triple,
|
||||
spec_path.to_path_buf(),
|
||||
code_gen_options,
|
||||
false,
|
||||
link_type,
|
||||
linking_strategy,
|
||||
true,
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
load_config,
|
||||
);
|
||||
let tempdir_res = tempfile::tempdir();
|
||||
|
||||
match res_binary_path {
|
||||
let res_binary_path = match tempdir_res {
|
||||
Ok(dylib_dir) => build_file(
|
||||
&arena,
|
||||
&triple,
|
||||
spec_path.to_path_buf(),
|
||||
code_gen_options,
|
||||
false,
|
||||
link_type,
|
||||
linking_strategy,
|
||||
true,
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
load_config,
|
||||
Some(dylib_dir.path()),
|
||||
),
|
||||
Err(_) => {
|
||||
eprintln!("`roc glue` was unable to create a tempdir.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let answer = match res_binary_path {
|
||||
Ok(BuiltFile {
|
||||
binary_path,
|
||||
problems,
|
||||
@ -196,7 +205,13 @@ pub fn generate(
|
||||
handle_error_module(module, total_time, spec_path.as_os_str(), true)
|
||||
}
|
||||
Err(BuildFileError::LoadingProblem(problem)) => handle_loading_problem(problem),
|
||||
}
|
||||
};
|
||||
|
||||
// Extend the lifetime of the tempdir to after we're done with everything,
|
||||
// so it doesn't get dropped before we're done reading from it!
|
||||
let _ = tempdir_res;
|
||||
|
||||
answer
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
|
Loading…
Reference in New Issue
Block a user