diff --git a/superide/proc.py b/superide/proc.py index 52246335a4e7c5c57dd96fea99a8164db11283e2..b7532fae4aa8040837000efc643b3b958e9d6a5b 100644 --- a/superide/proc.py +++ b/superide/proc.py @@ -109,24 +109,22 @@ def exec_command(*args, **kwargs): default = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE) default.update(kwargs) kwargs = default - try: - with subprocess.Popen(*args, **kwargs) as p: - try: - result["out"], result["err"] = p.communicate() - result["returncode"] = p.returncode - except KeyboardInterrupt as exc: - raise exception.AbortedByUser() from exc - finally: - for s in ("stdout", "stderr"): - if isinstance(kwargs[s], AsyncPipeBase): - kwargs[s].close() # pylint: disable=no-member - except FileNotFoundError as fnf: - raise exception.DockerNotFound() from fnf + + with subprocess.Popen(*args, **kwargs) as p: + try: + result["out"], result["err"] = p.communicate() + result["returncode"] = p.returncode + except KeyboardInterrupt as exc: + raise exception.AbortedByUser() from exc + finally: + for s in ("stdout", "stderr"): + if isinstance(kwargs[s], AsyncPipeBase): + kwargs[s].close() # pylint: disable=no-member + for s in ("stdout", "stderr"): if isinstance(kwargs[s], AsyncPipeBase): result[s[3:]] = kwargs[s].get_buffer() # pylint: disable=no-member - # if(result["out"].decode().find("not be found")!=-1): - # result["returncode"] = -1 + for key, value in result.items(): if isinstance(value, bytes): try: diff --git a/superide/project/commands/init.py b/superide/project/commands/init.py index 8907763cdb777bffe187f769af32dc669a36342e..f5dc22c838f2f8da3d20ba865e5725aa9de2f616 100644 --- a/superide/project/commands/init.py +++ b/superide/project/commands/init.py @@ -33,19 +33,7 @@ from superide.project.helpers import is_platformio_project # from superide.project.integration.generator import ProjectGenerator from superide.project.options import ProjectOptions from superide.project.vcsclient import VCSClientFactory - -def validate_boards(ctx, param, value): # pylint: disable=unused-argument - # pm = PlatformPackageManager() - # for id_ in value: - # try: - # pm.board_config(id_) - # except UnknownBoard as exc: - # raise click.BadParameter( - # "`%s`. Please search for board ID using `superide boards` " - # "command" % id_ - # ) from exc - return value - +from superide.toolchain.toolchain import Toolchain @click.command("init", short_help="Initialize a project or update existing") @click.option( @@ -54,6 +42,8 @@ def validate_boards(ctx, param, value): # pylint: disable=unused-argument default=os.getcwd, type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True), ) +@click.option( + "--env_image", "-i", help="Development environment container image name") @click.option("-e", "--environment", help="Update existing environment") @click.option( "-O", @@ -68,6 +58,7 @@ def validate_boards(ctx, param, value): # pylint: disable=unused-argument @click.option("-s", "--silent", is_flag=True) def project_init_cmd( project_dir, + env_image, environment, project_options, sample_code, @@ -80,9 +71,8 @@ def project_init_cmd( if is_new_project: if not silent: print_header(project_dir) - # 初始化项目,增加配置文件以及include、lib、src目录等 - # 但是这个目录结构的通用性存疑? - init_base_project(project_dir) + # 从容器镜像拷贝示例项目 + Toolchain(env_image, project_dir).init_project() with fs.cd(project_dir): if environment: @@ -93,16 +83,10 @@ def project_init_cmd( # resolve project dependencies if not no_install_dependencies and (environment): - install_project_dependencies( - options=dict( - project_dir=project_dir, - environments=[environment] if environment else [], - silent=silent, - ) - ) - - if sample_code: - init_sample_code(config, environment, sample_code) + for env in config.envs(): + env_image = config.get(f"env:{env}", "env_image") + if env_image: + Toolchain(env_image, project_dir).check_image() if is_new_project: init_cvs_ignore() @@ -136,141 +120,6 @@ def print_footer(is_new_project): ) -def init_base_project(project_dir): - with fs.cd(project_dir): - config = ProjectConfig() - config.save() - dir_to_readme = [ - (config.get("superide", "src_dir"), None), - (config.get("superide", "include_dir"), init_include_readme), - (config.get("superide", "lib_dir"), init_lib_readme), - (config.get("superide", "test_dir"), init_test_readme), - ] - for path, cb in dir_to_readme: - if os.path.isdir(path): - continue - os.makedirs(path) - if cb: - cb(path) - - -def init_include_readme(include_dir): - with open(os.path.join(include_dir, "README"), mode="w", encoding="utf8") as fp: - fp.write( - """ -This directory is intended for project header files. - -A header file is a file containing C declarations and macro definitions -to be shared between several project source files. You request the use of a -header file in your project source file (C, C++, etc) located in `src` folder -by including it, with the C preprocessing directive `#include'. - -```src/main.c - -#include "header.h" - -int main (void) -{ - ... -} -``` - -Including a header file produces the same results as copying the header file -into each source file that needs it. Such copying would be time-consuming -and error-prone. With a header file, the related declarations appear -in only one place. If they need to be changed, they can be changed in one -place, and programs that include the header file will automatically use the -new version when next recompiled. The header file eliminates the labor of -finding and changing all the copies as well as the risk that a failure to -find one copy will result in inconsistencies within a program. - -In C, the usual convention is to give header files names that end with `.h'. -It is most portable to use only letters, digits, dashes, and underscores in -header file names, and at most one dot. - -Read more about using header files in official GCC documentation: - -* Include Syntax -* Include Operation -* Once-Only Headers -* Computed Includes - -https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html -""", - ) - - -def init_lib_readme(lib_dir): - with open(os.path.join(lib_dir, "README"), mode="w", encoding="utf8") as fp: - fp.write( - """ -This directory is intended for project specific (private) libraries. -superide will compile them to static libraries and link into executable file. - -The source code of each library should be placed in a an own separate directory -("lib/your_library_name/[here are source files]"). - -For example, see a structure of the following two libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional, custom build options, etc) https://docs.superide.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -and a contents of `src/main.c`: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -superide Library Dependency Finder will find automatically dependent -libraries scanning project source files. - -More information about superide Library Dependency Finder -- https://docs.superide.org/page/librarymanager/ldf.html -""", - ) - - -def init_test_readme(test_dir): - with open(os.path.join(test_dir, "README"), mode="w", encoding="utf8") as fp: - fp.write( - """ -This directory is intended for superide Test Runner and project tests. - -Unit Testing is a software testing method by which individual units of -source code, sets of one or more MCU program modules together with associated -control data, usage procedures, and operating procedures, are tested to -determine whether they are fit for use. Unit testing finds problems early -in the development cycle. - -More information about superide Unit Testing: -- https://docs.superide.org/en/latest/advanced/unit-testing/index.html -""", - ) - - def init_cvs_ignore(): conf_path = ".gitignore" if os.path.isfile(conf_path): @@ -308,51 +157,3 @@ def update_project_env(environment, extra_project_options=None): config.save() - -def init_sample_code(config, environment, sample_code): - # try: - # p = PlatformFactory.from_env(environment) - # return p.generate_sample_code(config, environment) - # except (NotImplementedError, UndefinedEnvPlatformError): - # pass - if sample_code!="": - src_dir = config.get("superide", "src_dir") - if os.listdir(src_dir): - shutil.rmtree(src_dir) - os.makedirs(src_dir) - vcs = VCSClientFactory.new(src_dir, sample_code) - assert vcs.export() - return True - framework = config.get(f"env:{environment}", "framework", None) - if framework != ["arduino"]: - return None - main_content = """ -#include - -// put function declarations here: -int myFunction(int, int); - -void setup() { - // put your setup code here, to run once: - int result = myFunction(2, 3); -} - -void loop() { - // put your main code here, to run repeatedly: -} - -// put function definitions here: -int myFunction(int x, int y) { - return x + y; -} -""" - is_cpp_project = p.name not in ["intel_mcs51", "ststm8"] - src_dir = config.get("superide", "src_dir") - main_path = os.path.join(src_dir, "main.%s" % ("cpp" if is_cpp_project else "c")) - if os.path.isfile(main_path): - return None - if not os.path.isdir(src_dir): - os.makedirs(src_dir) - with open(main_path, mode="w", encoding="utf8") as fp: - fp.write(main_content.strip()) - return True diff --git a/superide/run/cli.py b/superide/run/cli.py index 88bce60fe8670aa151ab9297cabf5909d4ef85cf..4acb93f401671d50134424aa5a9be9511f90648b 100644 --- a/superide/run/cli.py +++ b/superide/run/cli.py @@ -75,6 +75,7 @@ except NotImplementedError: @click.option("--list-targets", is_flag=True) @click.option("-s", "--silent", is_flag=True) @click.option("-v", "--verbose", is_flag=True) +@click.argument('command',nargs=-1) @click.pass_context def cli( ctx, @@ -90,6 +91,7 @@ def cli( list_targets, silent, verbose, + command ): app.set_session_var("custom_project_conf", project_conf) @@ -126,7 +128,6 @@ def cli( # print empty line between multi environment project if not silent and any(r.get("succeeded") is not None for r in results): click.echo() - results.append( process_env( ctx, @@ -141,6 +142,7 @@ def cli( silent, verbose, project_dir, + command ) ) command_failed = any(r.get("succeeded") is False for r in results) @@ -157,7 +159,6 @@ def cli( if command_failed: raise exception.ReturnErrorCode(1) - return True @@ -174,6 +175,7 @@ def process_env( silent, verbose, project_dir, + command ): if not is_test_running and not silent: print_processing_header(name, config, verbose) @@ -195,6 +197,7 @@ def process_env( silent, verbose, project_dir, + command ).process() # if result["succeeded"] and "monitor" in targets and "nobuild" not in targets: diff --git a/superide/run/processor.py b/superide/run/processor.py index 41c4e9433955fdb41f0d5794be18045068271c56..117450d38225ddee5db8cb0ac9860e0bde8d762b 100644 --- a/superide/run/processor.py +++ b/superide/run/processor.py @@ -38,6 +38,7 @@ class EnvironmentProcessor: silent, verbose, project_dir, + command ): self.cmd_ctx = cmd_ctx self.name = name @@ -49,6 +50,7 @@ class EnvironmentProcessor: self.silent = silent self.verbose = verbose self.project_dir = project_dir + self.command = command self.options = config.items(env=name, as_dict=True) def get_build_variables(self): @@ -87,8 +89,18 @@ class EnvironmentProcessor: projectdir=self.project_dir dockername=self.name - command=Toolchain(dockername,projectdir).build() - result = self.CommandRun(command) + if self.command: + command = Toolchain(dockername,projectdir).container_command(" ".join(self.command)) + result = self.CommandRun(command) + else: + build_command=Toolchain(dockername,projectdir).build() + result = self.CommandRun(build_command) + check_command=Toolchain(dockername,projectdir).check() + result = self.CommandRun(check_command) + run_command=Toolchain(dockername,projectdir).run() + result = self.CommandRun(run_command) + + # (self.name, targets=build_targets, autoinstall=True).run return True diff --git a/superide/toolchain/toolchain.py b/superide/toolchain/toolchain.py index da0fabf4993d63d9fea0a92ac81065ce1ea542ce..72bb3b8d17b46e98b7481fa09efb19d66de7777e 100644 --- a/superide/toolchain/toolchain.py +++ b/superide/toolchain/toolchain.py @@ -1,5 +1,7 @@ import json import subprocess +import os +import shutil class Toolchain: def __init__(self, image_name, project_directory): @@ -7,7 +9,6 @@ class Toolchain: self.project_directory = project_directory self.contain_project_directory = "/app/project" self.check_image() - self._get_tools() def check_image(self): try: @@ -19,10 +20,34 @@ class Toolchain: # 拉取镜像 subprocess.run(['docker', 'pull', self.image_name]) - except subprocess.CalledProcessError: - print("Failed to execute the command.") + print("Failed to get image.") + + def init_project(self): + container_name = 'CopyExampleProjectDemo' + source_path = '/app/ExampleProject' + destination_path = self.project_directory + + # 创建容器 + create_command = ['docker', 'create', '--name', container_name, self.image_name] + subprocess.run(create_command) + + # 复制文件 + copy_command = ['docker', 'cp', f'{container_name}:{source_path}', destination_path] + subprocess.run(copy_command) + # 删除容器(可选) + delete_command = ['docker', 'rm', container_name] + subprocess.run(delete_command) + + # 移动到项目目录 + source_path = destination_path + '/ExampleProject/' + for file in os.listdir(source_path): + src_file = os.path.join(source_path, file) + dst_file = os.path.join(destination_path, file) + shutil.move(src_file, dst_file) + clear_command = ['rm', '-rf', source_path] + subprocess.run(clear_command) def _get_tools(self): with open(f'{self.project_directory}/.vscode/tasks.json') as file: @@ -36,13 +61,17 @@ class Toolchain: self.run_task = task def build(self): + self._get_tools() build_command = self.build_task["command"]+ " " + " ".join(self.build_task["args"]) return self.container_command(build_command) def check(self): + self._get_tools() check_command = self.check_task["command"]+ " " + " ".join(self.check_task["args"]) return self.container_command(check_command) + def run(self): + self._get_tools() run_command = self.run_task["command"]+ " " + " ".join(self.run_task["args"]) return self.container_command(run_command)