Hide keyboard shortcuts

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. 

3 

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 

12 

13from errors import DppArgparseFileNotFoundError 

14 

15if TYPE_CHECKING: 

16 from taxonomy import Command, MetaData 

17 

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 

22 

23 

24class ValidateExportPath(argparse.Action): 

25 """ 

26 Validator for export path argument. 

27 """ 

28 

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) 

40 

41 

42class BuildCommandScript(DockerCommandScript): 

43 def __init__(self, verbose: bool, *args, **kwargs): 

44 super().__init__(*args, **kwargs) 

45 self._verbose = verbose 

46 

47 def before(self): 

48 pass 

49 

50 def step(self, linenr: int, line: str): 

51 if self._verbose: 

52 message.stdout_progress_detail(f"defects4cpp.build[{linenr}]: {line}") 

53 

54 def output(self, linenr: Optional[int], exit_code: Optional[int], stream: str): 

55 if not exit_code: 

56 return 

57 

58 if exit_code != 0: 

59 message.warning(__name__, f"build command exit with {exit_code}") 

60 

61 def after(self): 

62 pass 

63 

64 

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 

75 

76 def create(self) -> Generator[BuildCommandScript, None, None]: 

77 return ( 

78 BuildCommandScript(self.stream, cmd.type, cmd.lines) for cmd in self.command 

79 ) 

80 

81 

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 ) 

113 

114 def create_script_generator( 

115 self, args: argparse.Namespace 

116 ) -> DockerCommandScriptGenerator: 

117 metadata = args.metadata 

118 worktree = args.worktree 

119 

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) 

126 

127 def setup(self, generator: DockerCommandScriptGenerator): 

128 message.info(__name__, f"'{generator.metadata.name}'") 

129 message.stdout_progress(f"[{generator.metadata.name}] start building") 

130 

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) 

135 

136 message.info(__name__, "done") 

137 message.stdout_progress(f"[{generator.metadata.name}] done") 

138 

139 @property 

140 def help(self) -> str: 

141 return "Build local with a build tool from docker" 

142 

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" 

150 

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")