Skip to content

Commit 87103ab

Browse files
committed
Making building and linking tests fully parallel.
This uses similar code that is used withing the toolchains to parallelize the linking process of all the tests accross all the available CPUs. It also respects the `-j` parameter if you wish to limit the number of cores used.
1 parent f5fb485 commit 87103ab

File tree

1 file changed

+90
-40
lines changed

1 file changed

+90
-40
lines changed

tools/test_api.py

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from Queue import Queue, Empty
3838
from os.path import join, exists, basename, relpath
3939
from threading import Thread, Lock
40+
from multiprocessing import Pool, cpu_count
4041
from subprocess import Popen, PIPE
4142

4243
# Imports related to mbed build api
@@ -2068,6 +2069,27 @@ def norm_relative_path(path, start):
20682069
path = path.replace("\\", "/")
20692070
return path
20702071

2072+
2073+
def build_test_worker(*args, **kwargs):
2074+
bin_file = None
2075+
ret = {
2076+
'result': False,
2077+
'args': args,
2078+
'kwargs': kwargs
2079+
}
2080+
2081+
try:
2082+
bin_file = build_project(*args, **kwargs)
2083+
ret['result'] = True
2084+
ret['bin_file'] = bin_file
2085+
ret['kwargs'] = kwargs
2086+
2087+
except:
2088+
ret['reason'] = sys.exc_info()[1]
2089+
2090+
return ret
2091+
2092+
20712093
def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
20722094
clean=False, notify=None, verbose=False, jobs=1, macros=None,
20732095
silent=False, report=None, properties=None,
@@ -2095,58 +2117,86 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
20952117

20962118
result = True
20972119

2098-
map_outputs_total = list()
2120+
jobs_count = int(jobs if jobs else cpu_count())
2121+
p = Pool(processes=jobs_count)
2122+
2123+
results = []
20992124
for test_name, test_path in tests.iteritems():
21002125
test_build_path = os.path.join(build_path, test_path)
21012126
src_path = base_source_paths + [test_path]
21022127
bin_file = None
21032128
test_case_folder_name = os.path.basename(test_path)
21042129

2130+
args = (src_path, test_build_path, target, toolchain_name)
2131+
kwargs = {
2132+
'jobs': jobs,
2133+
'clean': clean,
2134+
'macros': macros,
2135+
'name': test_case_folder_name,
2136+
'project_id': test_name,
2137+
'report': report,
2138+
'properties': properties,
2139+
'verbose': verbose,
2140+
'app_config': app_config,
2141+
'build_profile': build_profile
2142+
}
21052143

2106-
try:
2107-
bin_file = build_project(src_path, test_build_path, target, toolchain_name,
2108-
jobs=jobs,
2109-
clean=clean,
2110-
macros=macros,
2111-
name=test_case_folder_name,
2112-
project_id=test_name,
2113-
report=report,
2114-
properties=properties,
2115-
verbose=verbose,
2116-
app_config=app_config,
2117-
build_profile=build_profile)
2118-
2119-
except NotSupportedException:
2120-
pass
2121-
except ToolException:
2122-
result = False
2123-
if continue_on_build_fail:
2124-
continue
2125-
else:
2126-
break
2144+
results.append(p.apply_async(build_test_worker, args, kwargs))
21272145

2128-
# If a clean build was carried out last time, disable it for the next build.
2129-
# Otherwise the previously built test will be deleted.
2130-
if clean:
2131-
clean = False
2132-
2133-
# Normalize the path
2134-
if bin_file:
2135-
bin_file = norm_relative_path(bin_file, execution_directory)
2136-
2137-
test_build['tests'][test_name] = {
2138-
"binaries": [
2139-
{
2140-
"path": bin_file
2141-
}
2142-
]
2143-
}
2146+
p.close()
2147+
result = True
2148+
itr = 0
2149+
while len(results):
2150+
itr += 1
2151+
if itr > 360000:
2152+
p.terminate()
2153+
p.join()
2154+
raise ToolException("Compile did not finish in 10 minutes")
2155+
2156+
sleep(0.01)
2157+
pending = 0
2158+
for r in results:
2159+
if r.ready() is True:
2160+
try:
2161+
worker_result = r.get()
2162+
results.remove(r)
2163+
2164+
2165+
for test_key in worker_result['kwargs']['report'][target_name][toolchain_name].keys():
2166+
report[target_name][toolchain_name][test_key] = worker_result['kwargs']['report'][target_name][toolchain_name][test_key]
2167+
2168+
if not worker_result['result'] and not isinstance(worker_result['reason'], NotSupportedException):
2169+
result = False
2170+
2171+
if worker_result['result'] and 'bin_file' in worker_result:
2172+
bin_file = norm_relative_path(worker_result['bin_file'], execution_directory)
2173+
2174+
test_build['tests'][worker_result['kwargs']['project_id']] = {
2175+
"binaries": [
2176+
{
2177+
"path": bin_file
2178+
}
2179+
]
2180+
}
2181+
2182+
# TODO: add 'Image: bin_file' print statement here
2183+
2184+
except ToolException, err:
2185+
if p._taskqueue.queue:
2186+
p._taskqueue.queue.clear()
2187+
sleep(0.5)
2188+
p.terminate()
2189+
p.join()
2190+
raise ToolException(err)
2191+
else:
2192+
pending += 1
2193+
if pending >= jobs_count:
2194+
break
21442195

2145-
print 'Image: %s'% bin_file
2196+
p.join()
21462197

21472198
test_builds = {}
21482199
test_builds["%s-%s" % (target_name, toolchain_name)] = test_build
2149-
21502200

21512201
return result, test_builds
21522202

0 commit comments

Comments
 (0)