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
21
22
os .getenv ("GITHUB_ACTIONS" ) is not None )
22
23
is_running_on_windows = "Windows" in platform .platform ()
23
24
is_running_on_arm64 = "arm64" in platform .machine ()
24
- build_dir = (Path (os .path .abspath (__file__ )).parents [2 ] / "build" )
25
+ repo_dir = Path (os .path .abspath (__file__ )).parents [2 ]
26
+ build_dir = repo_dir / "build"
27
+ examples_dir = repo_dir / "examples"
25
28
cache_dir = build_dir / "cache"
26
- global_options = {}
27
- if is_running_in_ci :
28
- global_options ["::build.path" ] = "build/"
29
- global_options [":::cache_dir" ] = str (cache_dir )
29
+ repo_file = repo_dir / "repo.lb"
30
+ option_collector_pattern = r'<!--(.+?)-->\n\s+<!-- *<(option|collect) +name="(.+?)">(.+?)</(?:option|collect)> *-->'
31
+ option_map = {"option" : "-D" , "collect" : "--collect" }
32
+ module_pattern = r'<!--(.+?)-->\n\s+<!-- *<module>(.+?)</module> *-->'
33
+ global_options = f" -D modm:build:build.path=build/ -D modm:build:scons:cache_dir={ cache_dir } " if is_running_in_ci else ""
34
+
30
35
31
36
def run_command (where , command , all_output = False ):
32
37
result = subprocess .run (command , shell = True , cwd = where , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
@@ -45,18 +50,45 @@ def enable(projects):
45
50
filtered_projects .append (project )
46
51
return filtered_projects
47
52
53
+ def prepare (project ):
54
+ build_path = build_dir / project .relative_to (examples_dir )
55
+ project_cfg = project .read_text ()
56
+ configs = set (re .findall (r"<extends>(.+?)</extends>" , project_cfg ))
57
+
58
+ if len (configs ) >= 2 :
59
+ output = ["=" * 90 , f"Preparing: { project .parent } \n " ]
60
+ config_options_collectors = re .findall (option_collector_pattern , project_cfg , flags = re .MULTILINE )
61
+ config_modules = re .findall (module_pattern , project_cfg , flags = re .MULTILINE )
62
+ generators = []
63
+ for config in sorted (configs ):
64
+ config_name = re .sub (r"[:-]+" , "_" , config )
65
+ new_project_xml = (project .parent / build_path / config_name .replace ("modm_" , "" ) / "project.xml" )
66
+ shutil .copytree (project .parent , new_project_xml .parent , dirs_exist_ok = True )
67
+ new_project_cfg = re .sub (r"<extends>.*?</extends>" , "" , project_cfg )
68
+ new_project_cfg = re .sub (r"<library>" , f"<library>\n <extends>{ config } </extends>" , new_project_cfg )
69
+ new_project_xml .write_text (new_project_cfg )
70
+ options = "" .join (f" { option_map [t ]} { k } ={ v } " for d ,t ,k ,v in config_options_collectors if config in d )
71
+ build_options = "" .join (f" -m { m } " for d ,m in config_modules if config in d )
72
+ lbuild_options = f"-r { repo_file } -D modm:build:build.path=build/" + options
73
+ generators .append ((project , config , lbuild_options , build_options , new_project_xml ))
74
+ output .append (f"- { config :30} { options } { build_options } " )
75
+ print ("\n " .join (output ))
76
+ return generators
77
+
78
+ if '<option name="modm:target">hosted-linux</option>' in project_cfg :
79
+ target = "hosted-" + platform .system ().lower ()
80
+ if is_running_on_arm64 : target += "-arm64"
81
+ return [(project , target , "-D modm:target=" + target , "" , project )]
82
+
83
+ return [(project , "project.xml" , "" , "" , project )]
48
84
49
85
def generate (project ):
50
- path = project .parent
51
- output = ["=" * 90 , "Generating: {}" .format (path )]
52
- options = " " .join ("-D{}={}" .format (k , v ) for k ,v in global_options .items ())
53
- # Compile Linux examples under macOS with hosted-darwin target
54
- if "hosted-linux" in project .read_text ():
55
- options += " -D:target=hosted-{}" .format (platform .system ().lower ())
56
- if is_running_on_arm64 : options += "-arm64"
57
- rc , ro = run_command (path , "lbuild {} build" .format (options ))
86
+ project , config , lbuild_options , build_options , project_xml = project
87
+ output = ["=" * 90 , f"Generating: { project .parent } for { config } " ]
88
+ cmd = f"lbuild { global_options } { lbuild_options } build { build_options } --no-log"
89
+ rc , ro = run_command (project_xml .parent , cmd )
58
90
print ("\n " .join (output + [ro ]))
59
- return None if rc else project
91
+ return None if rc else project_xml . resolve ()
60
92
61
93
def build (project ):
62
94
path = project .parent
@@ -68,14 +100,14 @@ def build(project):
68
100
commands .append ( ("make build" , "Make" ) )
69
101
elif ":build:cmake" in project_cfg and not is_running_on_windows :
70
102
build_dir = re .search (r'name=".+?:build:build.path">(.*?)</option>' , project_cfg )[1 ]
71
- cmd = "cmake -E make_directory {}/cmake-build-release; " . format ( build_dir )
72
- cmd += '(cd {}/cmake-build-release && cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" {}); ' . format ( build_dir , path .absolute ())
73
- cmd += "cmake --build {}/cmake-build-release" . format ( build_dir )
103
+ cmd = f "cmake -E make_directory { build_dir } /cmake-build-release; "
104
+ cmd += f '(cd { build_dir } /cmake-build-release && cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" { path .absolute ()} ); '
105
+ cmd += f "cmake --build { build_dir } /cmake-build-release"
74
106
commands .append ( (cmd , "CMake" ) )
75
107
76
108
rcs = 0
77
109
for command , build_system in commands :
78
- output = ["=" * 90 , "Building: {} with {}" . format ( path / " main.cpp" , build_system ) ]
110
+ output = ["=" * 90 , f "Building: { path . relative_to ( repo_dir ) } / main.cpp with { build_system } " ]
79
111
rc , ro = run_command (path , command )
80
112
rcs += rc
81
113
print ("\n " .join (output + [ro ]))
@@ -93,7 +125,7 @@ def run(project):
93
125
94
126
rcs = 0
95
127
for command , build_system in commands :
96
- output = ["=" * 90 , "Running: {} with {}" . format ( path / " main.cpp" , build_system ) ]
128
+ output = ["=" * 90 , f "Running: { path . relative_to ( repo_dir ) } / main.cpp with { build_system } " ]
97
129
rc , ro = run_command (path , command , all_output = True )
98
130
print ("\n " .join (output + [ro ]))
99
131
if "CI: run fail" in project_cfg :
@@ -104,7 +136,7 @@ def run(project):
104
136
return None if rcs else project
105
137
106
138
def compile_examples (paths , jobs , split , part ):
107
- print ("Using {}x parallelism" . format ( jobs ) )
139
+ print (f "Using { jobs } x parallelism" )
108
140
# Create build folder to prevent process race
109
141
cache_dir .mkdir (exist_ok = True , parents = True )
110
142
(cache_dir / "config" ).write_text ('{"prefix_len": 2}' )
@@ -122,9 +154,17 @@ def compile_examples(paths, jobs, split, part):
122
154
# Filter projects
123
155
projects = enable (projects )
124
156
157
+ # first prepare all projects
158
+ with ThreadPool (jobs ) as pool :
159
+ projects = pool .map (prepare , projects )
160
+ # Unlistify the project preparations
161
+ projects = [p for plist in projects for p in plist ]
162
+ results += projects .count (None )
163
+
125
164
# first generate all projects
126
165
with ThreadPool (jobs ) as pool :
127
166
projects = pool .map (generate , projects )
167
+ # Unlistify the project configs
128
168
results += projects .count (None )
129
169
130
170
# Filter projects for successful generation
0 commit comments