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