10
10
import os
11
11
import sys
12
12
import re
13
+ import shutil
13
14
import argparse
14
15
import platform
15
16
import subprocess
20
21
os .getenv ("TRAVIS" ) is not None or
21
22
os .getenv ("GITHUB_ACTIONS" ) is not None )
22
23
is_running_on_windows = "Windows" in platform .platform ()
23
- build_dir = (Path (os .path .abspath (__file__ )).parents [2 ] / "build" )
24
+ is_running_on_arm64 = "arm" in platform .machine ()
25
+ repo_dir = Path (os .path .abspath (__file__ )).parents [2 ]
26
+ build_dir = repo_dir / "build"
24
27
cache_dir = build_dir / "cache"
25
- global_options = {}
26
- if is_running_in_ci :
27
- global_options ["::build.path" ] = "build/"
28
- global_options [":::cache_dir" ] = str (cache_dir )
28
+ repo_file = repo_dir / "repo.lb"
29
+ option_collector_pattern = r'<!--(.+?)-->\n\s+<!-- *<(option|collect) +name="(.+?)">(.+?)</(?:option|collect)> *-->'
30
+ option_map = {"option" : "-D" , "collect" : "--collect" }
31
+ module_pattern = r'<!--(.+?)-->\n\s+<!-- *<module>(.+?)</module> *-->'
32
+ global_options = f" -D modm:build:build.path=build/ -D modm:build:scons:cache_dir={ cache_dir } " if is_running_in_ci else ""
33
+
29
34
30
35
def run_command (where , command , all_output = False ):
31
36
result = subprocess .run (command , shell = True , cwd = where , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
@@ -35,16 +40,46 @@ def run_command(where, command, all_output=False):
35
40
output += result .stderr .decode ("utf-8" , errors = "ignore" ).strip (" \n " )
36
41
return (result .returncode , output )
37
42
43
+
44
+ def prepare (project ):
45
+ project_cfg = project .read_text ()
46
+ configs = set (re .findall (r"<extends>(.+?)</extends>" , project_cfg ))
47
+
48
+ if len (configs ) >= 2 :
49
+ output = ["=" * 90 , f"Preparing: { project .parent } \n " ]
50
+ config_options_collectors = re .findall (option_collector_pattern , project_cfg , flags = re .MULTILINE )
51
+ config_modules = re .findall (module_pattern , project_cfg , flags = re .MULTILINE )
52
+ generators = []
53
+ for config in sorted (configs ):
54
+ config_name = re .sub (r"[:-]+" , "_" , config )
55
+ new_project_xml = re .search (r'<option +name="modm:build:build\.path">(.*?)</option>' , project_cfg )[1 ]
56
+ new_project_xml = (project .parent / new_project_xml / config_name .replace ("modm_" , "" ) / "project.xml" )
57
+ shutil .copytree (project .parent , new_project_xml .parent , dirs_exist_ok = True )
58
+ new_project_cfg = re .sub (r"<extends>.*?</extends>" , "" , project_cfg )
59
+ new_project_cfg = re .sub (r"<library>" , f"<library>\n <extends>{ config } </extends>" , new_project_cfg )
60
+ new_project_xml .write_text (new_project_cfg )
61
+ options = "" .join (f" { option_map [t ]} { k } ={ v } " for d ,t ,k ,v in config_options_collectors if config in d )
62
+ build_options = "" .join (f" -m { m } " for d ,m in config_modules if config in d )
63
+ lbuild_options = f"-r { repo_file } -D modm:build:build.path=build/" + options
64
+ generators .append ((project , config , lbuild_options , build_options , new_project_xml ))
65
+ output .append (f"- { config :30} { options } { build_options } " )
66
+ print ("\n " .join (output ))
67
+ return generators
68
+
69
+ if '<option name="modm:target">hosted-linux</option>' in project_cfg :
70
+ target = "hosted-" + platform .system ().lower ()
71
+ if is_running_on_arm64 : target += "-arm64"
72
+ return [(project , target , "-D modm:target=" + target , "" , project )]
73
+
74
+ return [(project , "project.xml" , "" , "" , project )]
75
+
38
76
def generate (project ):
39
- path = project .parent
40
- output = ["=" * 90 , "Generating: {}" .format (path )]
41
- options = " " .join ("-D{}={}" .format (k , v ) for k ,v in global_options .items ())
42
- # Compile Linux examples under macOS with hosted-darwin target
43
- if "hosted-linux" in project .read_text ():
44
- options += " -D:target=hosted-{}" .format (platform .system ().lower ())
45
- rc , ro = run_command (path , "lbuild {} build" .format (options ))
77
+ project , config , lbuild_options , build_options , project_xml = project
78
+ output = ["=" * 90 , f"Generating: { project .parent } for { config } " ]
79
+ cmd = f"lbuild { global_options } { lbuild_options } build { build_options } --no-log"
80
+ rc , ro = run_command (project_xml .parent , cmd )
46
81
print ("\n " .join (output + [ro ]))
47
- return None if rc else project
82
+ return None if rc else project_xml . resolve ()
48
83
49
84
def build (project ):
50
85
path = project .parent
@@ -56,14 +91,14 @@ def build(project):
56
91
commands .append ( ("make build" , "Make" ) )
57
92
elif ":build:cmake" in project_cfg :
58
93
build_dir = re .search (r'name=".+?:build:build.path">(.*?)</option>' , project_cfg )[1 ]
59
- cmd = "cmake -E make_directory {}/cmake-build-release; " . format ( build_dir )
60
- cmd += '(cd {}/cmake-build-release && cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" {}); ' . format ( build_dir , path .absolute ())
61
- cmd += "cmake --build {}/cmake-build-release" . format ( build_dir )
94
+ cmd = f "cmake -E make_directory { build_dir } /cmake-build-release; "
95
+ cmd += f '(cd { build_dir } /cmake-build-release && cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" { path .absolute ()} ); '
96
+ cmd += f "cmake --build { build_dir } /cmake-build-release"
62
97
commands .append ( (cmd , "CMake" ) )
63
98
64
99
rcs = 0
65
100
for command , build_system in commands :
66
- output = ["=" * 90 , "Building: {} with {}" . format ( path / " main.cpp" , build_system ) ]
101
+ output = ["=" * 90 , f "Building: { path . relative_to ( repo_dir ) } / main.cpp with { build_system } " ]
67
102
rc , ro = run_command (path , command )
68
103
rcs += rc
69
104
print ("\n " .join (output + [ro ]))
@@ -81,7 +116,7 @@ def run(project):
81
116
82
117
rcs = 0
83
118
for command , build_system in commands :
84
- output = ["=" * 90 , "Running: {} with {}" . format ( path / " main.cpp" , build_system ) ]
119
+ output = ["=" * 90 , f "Running: { path . relative_to ( repo_dir ) } / main.cpp with { build_system } " ]
85
120
rc , ro = run_command (path , command , all_output = True )
86
121
print ("\n " .join (output + [ro ]))
87
122
if "CI: run fail" in project_cfg :
@@ -92,7 +127,7 @@ def run(project):
92
127
return None if rcs else project
93
128
94
129
def compile_examples (paths , jobs , split , part ):
95
- print ("Using {}x parallelism" . format ( jobs ) )
130
+ print (f "Using { jobs } x parallelism" )
96
131
# Create build folder to prevent process race
97
132
cache_dir .mkdir (exist_ok = True , parents = True )
98
133
(cache_dir / "config" ).write_text ('{"prefix_len": 2}' )
@@ -108,9 +143,17 @@ def compile_examples(paths, jobs, split, part):
108
143
chunk_size = math .ceil (len (projects ) / args .split )
109
144
projects = projects [chunk_size * args .part :min (chunk_size * (args .part + 1 ), len (projects ))]
110
145
146
+ # first prepare all projects
147
+ with ThreadPool (jobs ) as pool :
148
+ projects = pool .map (prepare , projects )
149
+ # Unlistify the project preparations
150
+ projects = [p for plist in projects for p in plist ]
151
+ results += projects .count (None )
152
+
111
153
# first generate all projects
112
154
with ThreadPool (jobs ) as pool :
113
155
projects = pool .map (generate , projects )
156
+ # Unlistify the project configs
114
157
results += projects .count (None )
115
158
116
159
# Filter projects for successful generation
0 commit comments