use bollard::Docker;
use std::path::Path;
use std::{collections::HashSet, path::PathBuf};
use std::fs;
use crate::error::Error;
use crate::{DockerConfig, BuildOptions};
pub async fn build_target(
source_path: PathBuf,
destination_path: Option<PathBuf>,
) -> Result<String, Error> {
build_target_with_docker(source_path, destination_path, BuildOptions::default(), DockerConfig::default()).await
}
pub(crate) async fn build_target_with_docker(
source_path: PathBuf,
destination_path: Option<PathBuf>,
options: BuildOptions,
docker_config: DockerConfig,
) -> Result<String, Error> {
if let Some(dst_path) = &destination_path {
fs::create_dir_all(dst_path).map_err(|_| Error::InvalidDestinationPath)?;
}
let source_path = validated_source_path(source_path)?;
let package_name =
crate::manifests::package_name(&source_path).map_err(|_| Error::InvalidSourcePath)?;
let wasm_file = format!("{package_name}.wasm").replace('-', "_");
let docker_image_tag = docker_config
.tag
.unwrap_or(crate::docker::PCHAIN_COMPILE_IMAGE_TAGS[0].to_string());
if !crate::docker::PCHAIN_COMPILE_IMAGE_TAGS.contains(&docker_image_tag.as_str()) {
return Err(Error::UnkownDockerImageTag(docker_image_tag));
}
build_target_in_docker(source_path, destination_path, options, docker_image_tag, wasm_file).await
}
pub(crate) async fn build_target_without_docker(
source_path: PathBuf,
destination_path: Option<PathBuf>,
options: BuildOptions,
) -> Result<String, Error> {
if let Some(dst_path) = &destination_path {
fs::create_dir_all(dst_path).map_err(|_| Error::InvalidDestinationPath)?;
}
let source_path = validated_source_path(source_path)?;
let package_name =
crate::manifests::package_name(&source_path).map_err(|_| Error::InvalidSourcePath)?;
let wasm_file = format!("{package_name}.wasm").replace('-', "_");
build_target_by_cargo(source_path, destination_path, options, wasm_file).await
}
fn validated_source_path(source_path: PathBuf) -> Result<PathBuf, Error> {
let src_str = source_path.to_str().ok_or(Error::InvalidSourcePath)?;
let src_absolute_str =
crate::manifests::get_absolute_path(src_str).map_err(|_| Error::InvalidSourcePath)?;
Ok(Path::new(&src_absolute_str).to_path_buf())
}
async fn build_target_in_docker(
source_path: PathBuf,
destination_path: Option<PathBuf>,
options: BuildOptions,
docker_image_tag: String,
wasm_file: String,
) -> Result<String, Error> {
let mut dependencies = HashSet::new();
crate::manifests::get_dependency_paths(&source_path, &mut dependencies)?;
let container_name = crate::docker::random_container_name();
let docker = Docker::connect_with_local_defaults().map_err(|_| Error::DockerDaemonFailure)?;
let image_name = crate::docker::pull_image(&docker, &docker_image_tag).await?;
crate::docker::start_container(&docker, &container_name, image_name).await?;
let result = compile_contract_in_docker_container(
&docker,
&container_name,
dependencies,
source_path,
destination_path,
options,
&wasm_file,
)
.await;
let _ = crate::docker::remove_container(&docker, &container_name).await;
result.map(|_| wasm_file)
}
async fn compile_contract_in_docker_container(
docker: &Docker,
container_name: &str,
dependencies: impl IntoIterator<Item = String>,
source_path: PathBuf,
destination_path: Option<PathBuf>,
options: BuildOptions,
wasm_file: &str,
) -> Result<(), Error> {
for dependency in dependencies {
crate::docker::copy_files(docker, container_name, &dependency).await?;
}
crate::docker::copy_files(docker, container_name, source_path.to_str().unwrap()).await?;
let (result_in_docker, build_log) = crate::docker::build_contracts(
docker,
container_name,
source_path,
options.locked,
wasm_file,
)
.await?;
crate::docker::copy_files_from(
docker,
container_name,
&result_in_docker,
destination_path.clone(),
build_log
)
.await?;
Ok(())
}
async fn build_target_by_cargo(
source_path: PathBuf,
destination_path: Option<PathBuf>,
options: BuildOptions,
wasm_file: String,
) -> Result<String, Error> {
let temp_dir = crate::cargo::random_temp_dir_name();
std::fs::create_dir_all(temp_dir.as_path()).map_err(|_| Error::CreateTempDir)?;
let result = crate::cargo::build_contract(
&temp_dir,
source_path.as_path(),
destination_path,
options.locked,
&wasm_file,
);
let _ = std::fs::remove_dir_all(temp_dir);
result.map(|_| wasm_file)
}