Coverage for bugscpp/processor/build.py : 94%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Build command.
4Compile projects inside a container.
5"""
6import argparse
7import os
8import shutil
9from pathlib import Path
10from textwrap import dedent
11from typing import TYPE_CHECKING, Generator, List, Optional
13from errors import DppArgparseFileNotFoundError
15if TYPE_CHECKING:
16 from taxonomy import Command, MetaData
18from message import message
19from processor.core.argparser import create_common_project_parser
20from processor.core.command import DockerCommand, DockerCommandScript, DockerCommandScriptGenerator
21from processor.core.data import Worktree
24class ValidateExportPath(argparse.Action):
25 """
26 Validator for export path argument.
27 """
29 def __call__(
30 self,
31 parser: argparse.ArgumentParser,
32 namespace: argparse.Namespace,
33 values: Optional[Path],
34 option_string=None,
35 ):
36 values = values or Path(os.getcwd())
37 if not values.absolute().exists():
38 raise DppArgparseFileNotFoundError(str(values))
39 setattr(namespace, self.dest, values)
42class BuildCommandScript(DockerCommandScript):
43 def __init__(self, verbose: bool, *args, **kwargs):
44 super().__init__(*args, **kwargs)
45 self._verbose = verbose
47 def before(self):
48 pass
50 def step(self, linenr: int, line: str):
51 if self._verbose:
52 message.stdout_progress_detail(f"defects4cpp.build[{linenr}]: {line}")
54 def output(self, linenr: Optional[int], exit_code: Optional[int], stream: str):
55 if not exit_code:
56 return
58 if exit_code != 0:
59 message.warning(__name__, f"build command exit with {exit_code}")
61 def after(self):
62 pass
65class BuildCommandScriptGenerator(DockerCommandScriptGenerator):
66 def __init__(
67 self,
68 command: List["Command"],
69 metadata: "MetaData",
70 worktree: Worktree,
71 verbose: bool,
72 ):
73 super().__init__(metadata, worktree, verbose)
74 self.command = command
76 def create(self) -> Generator[BuildCommandScript, None, None]:
77 return (
78 BuildCommandScript(self.stream, cmd.type, cmd.lines) for cmd in self.command
79 )
82class BuildCommand(DockerCommand):
83 def __init__(self):
84 super().__init__(parser=create_common_project_parser())
85 self._export_path: Optional[Path] = None
86 # TODO: write argparse description in detail
87 self.parser.add_argument(
88 "-e",
89 "--export",
90 type=Path,
91 dest="export",
92 help="export build commands.",
93 nargs="?",
94 action=ValidateExportPath,
95 )
96 self.parser.add_argument(
97 "-u",
98 "--uid",
99 type=Path,
100 dest="uid",
101 help="set uid of user defects4cpp",
102 nargs="?",
103 )
104 self.parser.usage = (
105 "bugcpp.py build PATH [-j|--jobs=JOBS] [--coverage] [-v|--verbose] [-u|--uid=UID_DPP_DOCKER_USER] "
106 "[-e|--export[=EXPORT_PATH]]"
107 )
108 self.parser.description = dedent(
109 """\
110 Build project inside docker.
111 """
112 )
114 def create_script_generator(
115 self, args: argparse.Namespace
116 ) -> DockerCommandScriptGenerator:
117 metadata = args.metadata
118 worktree = args.worktree
120 self._export_path = args.export
121 common = metadata.common_capture if self._export_path else metadata.common
122 command = (
123 common.build_coverage_command if args.coverage else common.build_command
124 )
125 return BuildCommandScriptGenerator(command, metadata, worktree, args.verbose)
127 def setup(self, generator: DockerCommandScriptGenerator):
128 message.info(__name__, f"'{generator.metadata.name}'")
129 message.stdout_progress(f"[{generator.metadata.name}] start building")
131 def teardown(self, generator: DockerCommandScriptGenerator):
132 if self._export_path:
133 message.info(__name__, f"export to '{str(self._export_path)}'")
134 self._find_compile_commands_json(generator.worktree.host, self._export_path)
136 message.info(__name__, "done")
137 message.stdout_progress(f"[{generator.metadata.name}] done")
139 @property
140 def help(self) -> str:
141 return "Build local with a build tool from docker"
143 @staticmethod
144 def _find_compile_commands_json(host: Path, dest: Path):
145 build_dir = host / "build"
146 if build_dir.exists():
147 compile_commands = build_dir / "compile_commands.json"
148 else:
149 compile_commands = host / "compile_commands.json"
151 if compile_commands.exists():
152 shutil.copyfile(str(compile_commands), str(dest / "compile_commands.json"))
153 else:
154 message.warning(__name__, "compile_commands.json could not be found")