diff --git a/CLI.md b/CLI.md index 178324a6..47628693 100644 --- a/CLI.md +++ b/CLI.md @@ -64,7 +64,7 @@ COMMANDS summary ---------------- new Create a new Smart Contract project based on a template. templates List the available Smart Contract templates. -build Build a Smart Contract project using the appropriate buildchain. +build Build a Smart Contract project. clean Clean a Smart Contract project. test Run scenarios (tests). report Print a detailed report of the smart contracts. @@ -114,7 +114,7 @@ options: $ mxpy contract build --help usage: mxpy contract build [-h] ... -Build a Smart Contract project using the appropriate buildchain. +Build a Smart Contract project. options: -h, --help show this help message and exit diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 27be0001..0c7325d2 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -43,7 +43,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: sub.set_defaults(func=list_templates) sub = cli_shared.add_command_subparser(subparsers, "contract", "build", - "Build a Smart Contract project using the appropriate buildchain.") + "Build a Smart Contract project.") _add_build_options_sc_meta(sub) sub.set_defaults(func=build) diff --git a/multiversx_sdk_cli/projects/__init__.py b/multiversx_sdk_cli/projects/__init__.py index 0607f0cd..db23619f 100644 --- a/multiversx_sdk_cli/projects/__init__.py +++ b/multiversx_sdk_cli/projects/__init__.py @@ -1,12 +1,8 @@ from multiversx_sdk_cli.projects.core import (build_project, clean_project, - get_projects_in_workspace, load_project, run_tests) from multiversx_sdk_cli.projects.project_base import Project -from multiversx_sdk_cli.projects.project_clang import ProjectClang -from multiversx_sdk_cli.projects.project_cpp import ProjectCpp from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.project_sol import ProjectSol from multiversx_sdk_cli.projects.report.do_report import do_report from multiversx_sdk_cli.projects.templates import Contract -__all__ = ["build_project", "clean_project", "do_report", "run_tests", "get_projects_in_workspace", "load_project", "Project", "ProjectClang", "ProjectCpp", "ProjectRust", "ProjectSol", "Contract"] +__all__ = ["build_project", "clean_project", "do_report", "run_tests", "load_project", "Project", "ProjectRust", "Contract"] diff --git a/multiversx_sdk_cli/projects/core.py b/multiversx_sdk_cli/projects/core.py index a9cffb88..ebba16e6 100644 --- a/multiversx_sdk_cli/projects/core.py +++ b/multiversx_sdk_cli/projects/core.py @@ -2,15 +2,12 @@ from pathlib import Path from typing import Any, List -from multiversx_sdk_cli import dependencies, errors, guards, utils +from multiversx_sdk_cli import dependencies, errors, guards from multiversx_sdk_cli.projects import shared from multiversx_sdk_cli.projects.constants import (OLD_PROJECT_CONFIG_FILENAME, PROJECT_CONFIG_FILENAME) from multiversx_sdk_cli.projects.project_base import Project -from multiversx_sdk_cli.projects.project_clang import ProjectClang -from multiversx_sdk_cli.projects.project_cpp import ProjectCpp from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.project_sol import ProjectSol logger = logging.getLogger("projects.core") @@ -18,12 +15,6 @@ def load_project(directory: Path) -> Project: guards.is_directory(directory) - if shared.is_source_clang(directory): - return ProjectClang(directory) - if shared.is_source_cpp(directory): - return ProjectCpp(directory) - if shared.is_source_sol(directory): - return ProjectSol(directory) if shared.is_source_rust(directory): return ProjectRust(directory) else: @@ -35,7 +26,6 @@ def build_project(directory: Path, args: List[str]): logger.info("build_project.directory: %s", directory) - guards.is_directory(directory) project = load_project(directory) outputs = project.build(args) logger.info("Build ran.") @@ -65,23 +55,6 @@ def run_tests(project_path: Path, args: Any): project.run_tests(directory, wildcard) -def get_projects_in_workspace(workspace: Path) -> List[Project]: - guards.is_directory(workspace) - subfolders = utils.get_subfolders(workspace) - projects = [] - - for folder in subfolders: - project_directory = workspace / folder - - try: - project = load_project(project_directory) - projects.append(project) - except Exception: - pass - - return projects - - def get_project_paths_recursively(base_path: Path) -> List[Path]: guards.is_directory(base_path) old_markers = list(base_path.glob(f"**/{OLD_PROJECT_CONFIG_FILENAME}")) diff --git a/multiversx_sdk_cli/projects/project_clang.py b/multiversx_sdk_cli/projects/project_clang.py deleted file mode 100644 index 1b800b7c..00000000 --- a/multiversx_sdk_cli/projects/project_clang.py +++ /dev/null @@ -1,158 +0,0 @@ -import logging -import subprocess -from os import path -from pathlib import Path -from typing import List - -from multiversx_sdk_cli import dependencies, errors, myprocess, utils -from multiversx_sdk_cli.projects.project_base import Project, rename_wasm_files -from multiversx_sdk_cli.projects.shared import \ - check_clang_and_cpp_dependencies_installed - -logger = logging.getLogger('ProjectClang') - - -class ProjectClang(Project): - - def __init__(self, directory: Path): - super().__init__(directory) - - def perform_build(self): - self.config = self.load_config() - self.ensure_source_files() - - self.unit = self.get_unit_file() - self.file_ll = self.unit.with_suffix('.ll') - self.file_o = self.unit.with_suffix('.o') - self.file_export = self.unit.with_suffix('.export') - self.file_output = self.unit.with_suffix('.wasm') - - try: - check_clang_and_cpp_dependencies_installed() - - self.do_clang() - self.do_llvm_link() - self.do_llc() - self.do_wasm() - except subprocess.CalledProcessError as err: - raise errors.BuildError(err.output) - - def do_clang(self): - logger.info('do_clang') - - tool = path.join(self._get_llvm_path(), 'clang-9') - args = [ - tool, - '-cc1', '-emit-llvm', - '-triple=wasm32-unknown-unknown-wasm', - ] - - if self.options.get('optimized', False): - args.append('-Ofast') - else: - args.append('-O0') - - args.extend(map(str, self.get_source_files())) - myprocess.run_process(args) - - def do_llvm_link(self): - logger.info('do_llvm_link') - tool = path.join(self._get_llvm_path(), 'llvm-link') - args = [tool] - args.extend(['-o', str(self.file_ll)]) - args.extend(map(str, self.get_ll_files())) - myprocess.run_process(args) - - def do_llc(self): - logger.info('do_llc') - tool = path.join(self._get_llvm_path(), 'llc') - args = [tool] - - if self.options.get('optimized', False): - args.append('-O3') - else: - args.append('-O0') - - args.append('-filetype=obj') - args.append(str(self.file_ll)) - - args.extend(['-o', str(self.file_o)]) - myprocess.run_process(args) - - def do_wasm(self): - logger.info('do_wasm') - tool = path.join(self._get_llvm_path(), 'wasm-ld') - args = [ - tool, - '--no-entry', - str(self.file_o), - '-o', str(self.file_output), - '--strip-all', - '-allow-undefined' - ] - - if self.options.get('verbose', False): - args.append('--verbose') - - logger.info('exported functions:') - for export in self.get_exported_functions(): - logger.info(f'\t{export}') - args.append(f'-export={export}') - - myprocess.run_process(args) - - def _do_after_build_custom(self) -> List[Path]: - output_wasm_file = self._copy_to_output(self.file_output) - self.file_output.unlink() - self.file_ll.unlink() - self.file_o.unlink() - for ll_file in self.get_ll_files(): - try: - ll_file.unlink() - except FileNotFoundError: - pass - - paths = rename_wasm_files([output_wasm_file], self.options.get("wasm-name")) - return paths - - def _get_llvm_path(self): - return dependencies.get_module_directory('llvm') - - def get_source_files(self): - for filename in self.config['source_files']: - yield (self.path / filename).expanduser().resolve() - - def get_ll_files(self): - for source_file in self.get_source_files(): - yield source_file.with_suffix('.ll') - - def get_unit_file(self): - first_file = next(self.get_source_files()) - return first_file - - def ensure_source_files(self): - try: - source_files = self.config['source_files'] - if len(source_files) == 0: - source_files = self.get_source_files_from_folder() - except KeyError: - source_files = self.get_source_files_from_folder() - - self.config['source_files'] = source_files - - def get_exported_functions(self) -> List[str]: - file_export = self.find_file_globally('*.export') - lines = utils.read_lines(file_export) - return lines - - def default_config(self): - config = super().default_config() - config['language'] = 'clang' - config['source_files'] = self.get_source_files_from_folder() - return config - - def get_source_files_from_folder(self): - return list(map(str, self.path.rglob('*.c'))) - - def get_dependencies(self): - return [""] diff --git a/multiversx_sdk_cli/projects/project_cpp.py b/multiversx_sdk_cli/projects/project_cpp.py deleted file mode 100644 index eb2dafef..00000000 --- a/multiversx_sdk_cli/projects/project_cpp.py +++ /dev/null @@ -1,114 +0,0 @@ -import logging -import os -import subprocess -from os import path -from pathlib import Path -from typing import List - -from multiversx_sdk_cli import dependencies, errors, myprocess, utils -from multiversx_sdk_cli.projects.project_base import Project, rename_wasm_files -from multiversx_sdk_cli.projects.shared import \ - check_clang_and_cpp_dependencies_installed - -logger = logging.getLogger("ProjectCpp") - - -class ProjectCpp(Project): - def __init__(self, directory: Path): - super().__init__(directory) - - def perform_build(self): - self.build_configuration = CppBuildConfiguration(self, self.debug) - self.unit = self.find_file_globally("*.cpp") - self.file_ll = self.unit.with_suffix(".ll") - self.file_o = self.unit.with_suffix(".o") - self.file_export = self.unit.with_suffix(".export") - - try: - check_clang_and_cpp_dependencies_installed() - - self._do_clang() - self._do_llc() - self._do_wasm() - except subprocess.CalledProcessError as err: - raise errors.BuildError(err.output) - - def _do_clang(self): - logger.info("_do_clang") - tool = path.join(self._get_llvm_path(), "clang-9") - args = [ - tool, - "-cc1", "-emit-llvm", - "-triple=wasm32-unknown-unknown-wasm", - "-ObjC++", - "-std=c++17", - "-nostdinc++", - "-nobuiltininc", - "-fno-builtin", - ] - if self.options.get("optimized", False): - args.append("-Ofast") - else: - args.append("-O0") - args.append(str(self.unit)) - myprocess.run_process(args) - - def _do_llc(self): - logger.info("_do_llc") - tool = path.join(self._get_llvm_path(), "llc") - args = [tool] - if self.options.get("optimized", False): - args.append("-O3") - else: - args.append("-O0") - args.extend(["-filetype=obj", self.file_ll, "-o", self.file_o]) - myprocess.run_process(args) - - def _do_wasm(self): - logger.info("_do_wasm") - tool = path.join(self._get_llvm_path(), "wasm-ld") - args = [ - tool, - "--no-entry", - str(self.file_o), - "-o", self.find_file_globally("*.cpp").with_suffix(".wasm"), - "--strip-all", - "--allow-undefined", - "--demangle" - ] - - if self.options.get("verbose", False): - args.append("--verbose") - - for export in self.build_configuration.exports: - args.append(f"-export={export}") - - myprocess.run_process(args) - - def _do_after_build_custom(self) -> List[Path]: - source_file = self.find_file_globally("*.cpp") - output_wasm_file = self._copy_to_output(source_file.with_suffix(".wasm")) - os.remove(source_file.with_suffix(".wasm")) - os.remove(source_file.with_suffix(".ll")) - os.remove(source_file.with_suffix(".o")) - - paths = rename_wasm_files([output_wasm_file], self.options.get("wasm-name")) - return paths - - def _get_llvm_path(self): - return dependencies.get_module_directory("llvm") - - def get_dependencies(self): - return [""] - - -class CppBuildConfiguration: - def __init__(self, project: Project, debug): - self.project = project - self.debug = debug - self.exports = self._get_exports() - - def _get_exports(self): - file_export = self.project.find_file_globally("*.export") - lines = utils.read_lines(file_export) - return lines diff --git a/multiversx_sdk_cli/projects/project_rust.py b/multiversx_sdk_cli/projects/project_rust.py index cc837eb1..7825d5d6 100644 --- a/multiversx_sdk_cli/projects/project_rust.py +++ b/multiversx_sdk_cli/projects/project_rust.py @@ -1,7 +1,7 @@ import logging import subprocess from pathlib import Path -from typing import Any, Dict, List, Set, cast +from typing import Any, Dict, List from multiversx_sdk_cli import dependencies, errors, utils, workstation from multiversx_sdk_cli.constants import DEFAULT_CARGO_TARGET_DIR_NAME @@ -14,7 +14,6 @@ class ProjectRust(Project): def __init__(self, directory: Path): super().__init__(directory) - self.cargo_file = self.get_cargo_file() def clean(self): env = self.get_env() @@ -29,10 +28,6 @@ def clean(self): subprocess.check_call(args, env=env) - def get_cargo_file(self): - cargo_path = self.path / 'Cargo.toml' - return CargoFile(cargo_path) - def get_meta_folder(self): return self.path / 'meta' @@ -91,21 +86,7 @@ def get_abi_filepath(self): def get_abi_folder(self): return Path(self.directory, "abi") - def get_wasm_default_name(self, suffix: str = "") -> str: - return f"{self.cargo_file.package_name}{suffix}.wasm" - def _do_after_build_custom(self) -> List[Path]: - if not self.has_meta(): - base_name = str(self.cargo_file.package_name) - temporary_wasm_base_name = base_name.replace("-", "_") - wasm_file = self.get_wasm_path(f"{temporary_wasm_base_name}_wasm.wasm") - wasm_file.rename(self.get_wasm_default_path()) - - if self.has_abi(): - abi_file = self.get_abi_filepath() - abi_file_renamed = Path(self.get_output_folder(), f"{base_name}.abi.json") - abi_file.rename(abi_file_renamed) - outputs = [self.get_wasm_default_path()] if self.has_wasm_view(): outputs.append(self.get_wasm_view_default_path()) @@ -140,114 +121,3 @@ def build_wasm_with_debug_symbols(self, build_options: Dict[str, Any]): subprocess.check_call(args, env=env, cwd=cwd) except subprocess.CalledProcessError as err: raise errors.BuildError(f"error code = {err.returncode}, see output") - - -class CargoFile: - data: Dict[str, Any] - - def __init__(self, path: Path): - self.data = {} - self.path = path - - try: - self._parse_file() - except Exception as err: - raise errors.BuildError("Can't read or parse [Cargo.toml] file", err) - - def _parse_file(self): - self.data = utils.read_toml_file(self.path) - - @property - def package_name(self): - return self._get_package().get("name") - - @package_name.setter - def package_name(self, value): - self._get_package().update({"name": value}) - - @property - def version(self): - return self._get_package().get("version") - - @version.setter - def version(self, value): - self._get_package().update({"version": value}) - - @property - def authors(self): - return self._get_package().get("authors") - - @authors.setter - def authors(self, value): - self._get_package().update({"authors": value}) - - @property - def edition(self): - return self._get_package().get("edition") - - @edition.setter - def edition(self, value): - self._get_package().update({"edition": value}) - - @property - def publish(self): - return self._get_package().get("publish") - - @publish.setter - def publish(self, value): - self._get_package().update({"publish": value}) - - def save(self): - utils.write_toml_file(self.path, self.data) - - def _get_package(self) -> Dict[str, Any]: - if "package" not in self.data: - self.data["package"] = {} - package = cast(Dict[str, Any], self.data['package']) - return package - - def get_dependencies(self) -> Dict[str, Any]: - if "dependencies" not in self.data: - self.data["dependencies"] = {} - dependencies = cast(Dict[str, Any], self.data['dependencies']) - return dependencies - - def get_dev_dependencies(self) -> Dict[str, Any]: - if "dev-dependencies" not in self.data: - self.data["dev-dependencies"] = {} - dev_dependencies = cast(Dict[str, Any], self.data['dev-dependencies']) - return dev_dependencies - - def get_dependency(self, name: str) -> Dict[str, Any]: - dependencies = self.get_dependencies() - dependency = cast(Dict[str, Any], dependencies.get(name)) - if dependency is None: - raise errors.BuildError(f"Can't get cargo dependency: {name}") - return dependency - - def get_dev_dependency(self, name) -> Dict[str, Any]: - dependencies = self.get_dev_dependencies() - dependency = cast(Dict[str, Any], dependencies.get(name)) - if dependency is None: - raise errors.BuildError(f"Can't get cargo dev-dependency: {name}") - return dependency - - -def paths_of(env: Dict[str, str], key: str) -> Set[str]: - try: - return set(env[key].split(":")) - except KeyError: - return set() - - -def merge_env(first: Dict[str, str], second: Dict[str, str]) -> Dict[str, str]: - """ ->>> merge_env({'PATH':'first:common', 'CARGO_PATH': 'cargo_path'}, {'PATH':'second:common', 'EXAMPLE': 'other'}) -{'CARGO_PATH': 'cargo_path', 'EXAMPLE': 'other', 'PATH': 'common:first:second'} - """ - keys = set(first.keys()).union(second.keys()) - merged: Dict[str, str] = dict() - for key in sorted(keys): - values = paths_of(first, key).union(paths_of(second, key)) - merged[key] = ":".join(sorted(values)) - return merged diff --git a/multiversx_sdk_cli/projects/project_sol.py b/multiversx_sdk_cli/projects/project_sol.py deleted file mode 100644 index 5bf2e6fc..00000000 --- a/multiversx_sdk_cli/projects/project_sol.py +++ /dev/null @@ -1,21 +0,0 @@ -import logging -from pathlib import Path -from typing import List - -from multiversx_sdk_cli.projects.project_base import Project - -logger = logging.getLogger("ProjectSol") - - -class ProjectSol(Project): - def __init__(self, directory: Path): - super().__init__(directory) - - def perform_build(self): - pass - - def get_dependencies(self) -> List[str]: - return [] - - def _do_after_build_custom(self) -> List[Path]: - raise NotImplementedError() diff --git a/multiversx_sdk_cli/projects/shared.py b/multiversx_sdk_cli/projects/shared.py index 83e52585..25cdbfd4 100644 --- a/multiversx_sdk_cli/projects/shared.py +++ b/multiversx_sdk_cli/projects/shared.py @@ -1,25 +1,9 @@ import logging -import shutil from pathlib import Path -from multiversx_sdk_cli.errors import KnownError -from multiversx_sdk_cli.ux import show_critical_error - logger = logging.getLogger("projects.shared") -def is_source_clang(directory: Path) -> bool: - return _directory_contains_file(directory, ".c") - - -def is_source_cpp(directory: Path) -> bool: - return _directory_contains_file(directory, ".cpp") - - -def is_source_sol(directory: Path) -> bool: - return _directory_contains_file(directory, ".sol") - - def is_source_rust(directory: Path) -> bool: return _directory_contains_file(directory, "Cargo.toml") @@ -29,27 +13,3 @@ def _directory_contains_file(directory: Path, name_suffix: str) -> bool: if str(file).lower().endswith(name_suffix.lower()): return True return False - - -def check_clang_and_cpp_dependencies_installed() -> None: - which_clang = shutil.which("clang") - which_llc = shutil.which("llc") - which_wasm_ld = shutil.which("wasm-ld") - which_llvm_link = shutil.which("llvm-link") - - logger.info(f"which_clang: {which_clang}") - logger.info(f"which_llc: {which_llc}") - logger.info(f"which_wasm_ld: {which_wasm_ld}") - logger.info(f"which_llvm_link: {which_llvm_link}") - - dependencies = [which_clang, which_llc, which_wasm_ld, which_llvm_link] - is_installed = all(dependency is not None for dependency in dependencies) - - if is_installed is False: - message = """ -`clang` is not installed. Please install it manually, then try again. -Check out the cookbook: https://docs.multiversx.com/sdk-and-tools/sdk-py/mxpy-cli -For more details check out this page: https://clang.llvm.org/get_started.html""" - - show_critical_error(message) - raise KnownError("The required dependencies are not installed. Please check the above message.")