summaryrefslogtreecommitdiff
path: root/scripts/test_build_extern.py
blob: e2c2cd87bb1452ccc5601940295aa7aa04b5675f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import argparse
import glob
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
import tempfile
from datetime import datetime

import argparse

parser = argparse.ArgumentParser(
    description='Test building the API repo and plugins out-of-tree (for CI checking of end-user workflow)'
)
parser.add_argument('--headless', default=False, action='store_true', help='Only include headless plugins')
parser.add_argument('-j', '--parallel', default=4, help='Number of parallel jobs to tell cmake to run.')
args = parser.parse_args()

try:
	# Make sure we have clang!
	subprocess.check_call(['clang', '-v'])
except subprocess.SubprocessError as e:
	print("Clang not found! Please install clang and add it to PATH")
	sys.exit(1)

if not args.headless:
	try:
		# Also Qt
		subprocess.check_call(['qmake', '--version'])
	except subprocess.SubprocessError as e:
		print("qmake not found! Please install Qt and add it to PATH")
		sys.exit(1)

api_base = Path(__file__).parent.parent.absolute()

configure_args = []
build_args = []
configure_env = os.environ.copy()

if platform.system() == "Windows":
	configure_env['CXXFLAGS'] = f'/MP{args.parallel}'
	configure_env['CFLAGS'] = f'/MP{args.parallel}'
else:
	build_args.extend(['-j', str(args.parallel)])

if args.headless:
	configure_args.extend(['-DHEADLESS=1'])

# Copy api out of the source tree and build it externally
with tempfile.TemporaryDirectory() as tempdir:
	temp_api_base = Path(tempdir) / 'binaryninjaapi'
	print(f'Copy {api_base} => {temp_api_base}')
	shutil.copytree(api_base, temp_api_base)

	# Clean up dirty repo
	if (temp_api_base / 'build').exists():
		shutil.rmtree(temp_api_base / 'build')

	# Now try to build
	try:
		subprocess.check_call(['cmake', '-B', 'build'] + configure_args, cwd=temp_api_base, env=configure_env)
		subprocess.check_call(['cmake', '--build', 'build'] + build_args, cwd=temp_api_base)
	finally:
		if (temp_api_base / 'build').exists():
			shutil.rmtree(temp_api_base / 'build')

	# Now try to build examples (in-tree)
	try:
		subprocess.check_call(['cmake', '-B', 'build', '-DBN_API_BUILD_EXAMPLES=1'] + configure_args, cwd=temp_api_base,
		                      env=configure_env)
		subprocess.check_call(['cmake', '--build', 'build'] + build_args, cwd=temp_api_base)
	finally:
		if (temp_api_base / 'build').exists():
			shutil.rmtree(temp_api_base / 'build')

	# Now try to build examples (out-of-tree)
	for f in glob.glob(str(api_base / 'examples' / '**' / 'CMakeLists.txt'), recursive=True):
		example_base = Path(f).parent
		if example_base == api_base / 'examples':
			continue

		# Check for headless
		if args.headless:
			with open(f, 'r') as cmake_file:
				# Assume any ui plugin has 'binaryninjaui' in its contents
				if 'binaryninjaui' in cmake_file.read():
					continue

		with tempfile.TemporaryDirectory() as tempexdir:
			temp_example_base = Path(tempexdir) / example_base.name
			print(f'Copy {example_base} => {temp_example_base} at {datetime.now()}')
			shutil.copytree(example_base, temp_example_base)

			if (temp_example_base / 'build').exists():
				shutil.rmtree(temp_example_base / 'build')

			try:
				subprocess.check_call(['cmake', '-B', 'build', f'-DBN_API_PATH={temp_api_base}'] + configure_args,
				                      cwd=temp_example_base, env=configure_env)
				subprocess.check_call(['cmake', '--build', 'build'] + build_args, cwd=temp_example_base)
			finally:
				if (temp_example_base / 'build').exists():
					shutil.rmtree(temp_example_base / 'build')