use crate::utils::dependency::{Dependency, DependencyDetails};
use crate::utils::helpers::{find_file_name, find_main_path};
use crate::{
cli::BuildCommand,
utils::dependency,
utils::helpers::{
get_main_file, print_on_failure, print_on_success, print_on_success_library, read_manifest,
},
};
use std::fs::File;
use std::io::Write;
use std::sync::Arc;
use sway_core::{FinalizedAsm, TreeType};
use sway_utils::{constants, find_manifest_dir};
use sway_core::{
create_module, BuildConfig, BytecodeCompilationResult, CompilationResult, NamespaceRef,
NamespaceWrapper,
};
use anyhow::Result;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
pub fn build(command: BuildCommand) -> Result<Vec<u8>, String> {
let this_dir = if let Some(ref path) = command.path {
PathBuf::from(path)
} else {
std::env::current_dir().map_err(|e| format!("{:?}", e))?
};
let BuildCommand {
binary_outfile,
use_ir,
print_finalized_asm,
print_intermediate_asm,
print_ir,
offline_mode,
silent_mode,
..
} = command;
let manifest_dir = match find_manifest_dir(&this_dir) {
Some(dir) => dir,
None => {
return Err(format!(
"No manifest file found in this directory or any parent directories of it: {:?}",
this_dir
))
}
};
let mut manifest = read_manifest(&manifest_dir)?;
let main_path = {
let mut code_dir = manifest_dir.clone();
code_dir.push(constants::SRC_DIR);
code_dir.push(&manifest.project.entry);
code_dir
};
let mut file_path = manifest_dir.clone();
file_path.pop();
let file_name = match main_path.strip_prefix(file_path.clone()) {
Ok(o) => o,
Err(err) => return Err(err.to_string()),
};
let build_config = BuildConfig::root_from_file_name_and_manifest_path(
file_name.to_path_buf(),
manifest_dir.clone(),
)
.use_ir(use_ir || print_ir) .print_finalized_asm(print_finalized_asm)
.print_intermediate_asm(print_intermediate_asm)
.print_ir(print_ir);
let mut dependency_graph = HashMap::new();
let namespace = create_module();
if let Some(ref mut deps) = manifest.dependencies {
for (dependency_name, dependency_details) in deps.iter_mut() {
compile_dependency_lib(
&this_dir,
dependency_name,
dependency_details,
namespace,
&mut dependency_graph,
silent_mode,
offline_mode,
)?;
}
}
let main_file = get_main_file(&manifest, &manifest_dir)?;
let main = compile(
main_file,
&manifest.project.name,
namespace,
build_config,
&mut dependency_graph,
silent_mode,
)?;
if let Some(outfile) = binary_outfile {
let mut file = File::create(outfile).map_err(|e| e.to_string())?;
file.write_all(main.as_slice()).map_err(|e| e.to_string())?;
}
println!(" Bytecode size is {} bytes.", main.len());
Ok(main)
}
fn compile_dependency_lib<'manifest>(
project_file_path: &Path,
dependency_name: &'manifest str,
dependency_lib: &mut Dependency,
namespace: NamespaceRef,
dependency_graph: &mut HashMap<String, HashSet<String>>,
silent_mode: bool,
offline_mode: bool,
) -> Result<(), String> {
let mut details = match dependency_lib {
Dependency::Simple(..) => {
return Err(
"Not yet implemented: Simple version-spec dependencies require a registry.".into(),
)
}
Dependency::Detailed(ref mut details) => details,
};
if let Some(ref git) = details.git {
let fully_qualified_dep_name = format!("{}-{}", dependency_name, git);
let downloaded_dep_path = match dependency::download_github_dep(
&fully_qualified_dep_name,
git,
&details.branch,
&details.version,
offline_mode.into(),
) {
Ok(path) => path,
Err(e) => {
return Err(format!(
"Couldn't download dependency ({:?}): {:?}",
dependency_name, e
))
}
};
details.path = Some(downloaded_dep_path);
}
let dep_path = match dependency_lib {
Dependency::Simple(..) => {
return Err(
"Not yet implemented: Simple version-spec dependencies require a registry.".into(),
)
}
Dependency::Detailed(DependencyDetails { path, .. }) => path,
};
let dep_path =
match dep_path {
Some(p) => p,
None => return Err(
"Only simple path imports are supported right now. Please supply a path relative \
to the manifest file."
.into(),
),
};
let mut project_path = PathBuf::from(project_file_path);
project_path.push(dep_path);
let manifest_dir = match find_manifest_dir(&project_path) {
Some(o) => o,
None => {
return Err(format!(
"Manifest not found for dependency {:?}.",
project_path
))
}
};
let mut manifest_of_dep = read_manifest(&manifest_dir)?;
let main_path = find_main_path(&manifest_dir, &manifest_of_dep);
let file_name = find_file_name(&manifest_dir, &main_path)?;
let build_config = BuildConfig::root_from_file_name_and_manifest_path(
file_name.to_path_buf(),
manifest_dir.clone(),
);
let dep_namespace = create_module();
if let Some(ref mut deps) = manifest_of_dep.dependencies {
for (dependency_name, ref mut dependency_lib) in deps {
compile_dependency_lib(
&manifest_dir,
dependency_name,
dependency_lib,
dep_namespace,
dependency_graph,
silent_mode,
offline_mode,
)?;
}
}
let main_file = get_main_file(&manifest_of_dep, &manifest_dir)?;
let compiled = compile_library(
main_file,
&manifest_of_dep.project.name,
dep_namespace,
build_config,
dependency_graph,
silent_mode,
)?;
namespace.insert_module_ref(dependency_name.to_string(), compiled);
Ok(())
}
fn compile_library(
source: Arc<str>,
proj_name: &str,
namespace: NamespaceRef,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
silent_mode: bool,
) -> Result<NamespaceRef, String> {
let res = sway_core::compile_to_asm(source, namespace, build_config, dependency_graph);
match res {
CompilationResult::Library {
namespace,
warnings,
..
} => {
print_on_success_library(silent_mode, proj_name, warnings);
Ok(namespace)
}
CompilationResult::Failure { errors, warnings } => {
print_on_failure(silent_mode, warnings, errors);
Err(format!("Failed to compile {}", proj_name))
}
_ => {
return Err(format!(
"Project \"{}\" was included as a dependency but it is not a library.",
proj_name
))
}
}
}
fn compile(
source: Arc<str>,
proj_name: &str,
namespace: NamespaceRef,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
silent_mode: bool,
) -> Result<Vec<u8>, String> {
let res = sway_core::compile_to_bytecode(source, namespace, build_config, dependency_graph);
match res {
BytecodeCompilationResult::Success { bytes, warnings } => {
print_on_success(silent_mode, proj_name, warnings, TreeType::Script {});
Ok(bytes)
}
BytecodeCompilationResult::Library { warnings } => {
print_on_success_library(silent_mode, proj_name, warnings);
Ok(vec![])
}
BytecodeCompilationResult::Failure { errors, warnings } => {
print_on_failure(silent_mode, warnings, errors);
Err(format!("Failed to compile {}", proj_name))
}
}
}
fn compile_to_asm(
source: Arc<str>,
proj_name: &str,
namespace: NamespaceRef,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
silent_mode: bool,
) -> Result<FinalizedAsm, String> {
let res = sway_core::compile_to_asm(source, namespace, build_config, dependency_graph);
match res {
CompilationResult::Success { asm, warnings } => {
print_on_success(silent_mode, proj_name, warnings, TreeType::Script {});
Ok(asm)
}
CompilationResult::Library { warnings, .. } => {
print_on_success_library(silent_mode, proj_name, warnings);
Ok(FinalizedAsm::Library)
}
CompilationResult::Failure { errors, warnings } => {
print_on_failure(silent_mode, warnings, errors);
return Err(format!("Failed to compile {}", proj_name));
}
}
}