microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ef598e5dbee969896aec0c2a72b77800cbedaa14

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

build_support/windows_cross/cross_tool.py

234lines · modecode

1#!/usr/bin/env python3
2
3# Copyright (c) Microsoft Corporation.
4# Licensed under the MIT License.
5
6'''
7Runs an LLVM tool (such as clang-cl) with the correct environment variables set
8for cross-compiling on Windows. Locates the necessary library and include paths
9from the Windows SDK and Visual Studio.
10
11This script must be linked to the desired tool name with the architecture
12prefixed, e.g. x86_64-clang-cl or aarch64-lld-link. It will then find the
13correct paths for the tool and run it.
14
15This can only be run from within WSL2.
16'''
17
18import subprocess
19import json
20import os
21import sys
22import tempfile
23import glob
24import argparse
25import re
26
27tools = ['clang-cl', 'lld-link', 'llvm-lib', 'llvm-dlltool', 'llvm-rc', 'midlrt.exe']
28
29
30def wslpath(p):
31 return subprocess.check_output(['wslpath', p]).decode('utf-8').strip()
32
33
34def reg(p):
35 output = subprocess.check_output(
36 ['reg.exe', 'query', p, '/v', 'KitsRoot10']).decode('utf-8')
37 # Looks like
38 # HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots
39 # KitsRoot10 REG_SZ C:\Program Files (x86)\Windows Kits\10\
40 # Get the C:\Program Files (x86)\Windows Kits\10\ part
41 return output.split('\n')[2].split(maxsplit=2)[2]
42
43
44def vs_paths(arch):
45 try:
46 if arch == 'x64':
47 component = "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
48 elif arch == 'arm64':
49 component = "Microsoft.VisualStudio.Component.VC.Tools.ARM64"
50 else:
51 raise Exception("Unknown architecture")
52 vswhere = wslpath(
53 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe')
54 info = json.loads(subprocess.check_output(
55 [vswhere, '-latest', '-products', '*', '-requires', component, '-format', 'json']).decode('utf-8').strip())[0]
56 install_path = wslpath(info['installationPath'])
57 version_file = f'{install_path}/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt'
58 with open(version_file) as f:
59 v = f.read().strip()
60 msvc_path = f'{install_path}/VC/Tools/MSVC/{v}'
61 lib = [f'{msvc_path}/lib/{arch}']
62 include = [f'{msvc_path}/include']
63 return {'lib': lib, 'include': include}
64 except:
65 raise Exception("Visual Studio not found")
66
67
68def sdk_paths(arch):
69 roots = wslpath(
70 reg('HKLM\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'))
71 versions = os.listdir(f'{roots}/Lib')
72 pattern = re.compile(r'^[\d.]+$')
73 versions = [v for v in versions if pattern.fullmatch(v)]
74 versions.sort()
75 version = versions[-1]
76 lib = [f'{roots}/Lib/{version}/{dir}/{arch}' for dir in ['ucrt', 'um']]
77 include = [f'{roots}/Include/{version}/{dir}' for dir in ['ucrt', 'um', 'shared', 'cppwinrt', 'winrt']]
78 bin = f'{roots}/bin/{version}/{arch}/'
79 return {'lib': lib, 'include': include, 'bin': bin}
80
81
82def check_config(a):
83 try:
84 if not all(os.path.isfile(path) for path in a['tools'].values() if path is not None):
85 return False
86 if not all(os.path.isdir(p) for p in a['lib'] + a['include']):
87 return False
88 return True
89 except:
90 return False
91
92
93def find_llvm_tool(name):
94 paths = os.environ.get("PATH").split(os.pathsep)
95 for p in paths:
96 if os.path.isfile(f'{p}/{name}'):
97 return f'{p}/{name}'
98 x = glob.glob(f'{p}/{name}-*')
99 if len(x) == 0:
100 continue
101 x.sort()
102 return x[-1]
103 return None
104
105
106def find_midlrt(sdk):
107 midlrt = f"{sdk['bin']}/midlrt.exe"
108 return os.path.normpath(midlrt)
109
110
111def get_config(arch, required_tool, ignore_cache):
112 cache_dir = os.environ.get(
113 'XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
114 cache_dir = f'{cache_dir}/windows-cross'
115 os.makedirs(cache_dir, exist_ok=True)
116 cache_file = f'{cache_dir}/cross-{arch}.json'
117
118 config = None
119 if not ignore_cache:
120 try:
121 mtime = os.path.getmtime(cache_file)
122 # If the mtime is older than this script, don't trust it.
123 if mtime > os.path.getmtime(__file__):
124 with open(cache_file) as f:
125 data = json.load(f)
126 if check_config(data):
127 config = data
128 except:
129 pass
130
131 if config is None or (required_tool and required_tool not in config['tools']):
132 if arch == 'x86_64':
133 win_arch = 'x64'
134 elif arch == 'aarch64':
135 win_arch = 'arm64'
136 else:
137 raise Exception("Unknown architecture")
138 sdk = sdk_paths(win_arch)
139 tool_paths = {}
140 config = {}
141 for tool in tools:
142 if tool == 'midlrt.exe':
143 p = find_midlrt(sdk)
144 else:
145 p = find_llvm_tool(tool)
146 if p:
147 tool_paths[tool] = p
148
149 if required_tool and required_tool not in tool_paths:
150 raise Exception(
151 f"tool '{required_tool}' not found, try installing it")
152
153 vs = vs_paths(win_arch)
154 config = {'lib': [os.path.normpath(p) for p in vs['lib'] + sdk['lib']],
155 'include': [os.path.normpath(p) for p in vs['include'] + sdk['include']],
156 'tools': tool_paths,
157 'sdk': [os.path.normpath(sdk['bin'])]}
158
159 if not check_config(config):
160 raise Exception("invalid paths")
161
162 [f, p] = tempfile.mkstemp(dir=cache_dir)
163 with os.fdopen(f, 'w') as f:
164 json.dump(config, f)
165 os.rename(p, cache_file)
166
167 return config
168
169
170name = os.path.basename(sys.argv[0])
171arch = None
172ignore_cache = False
173action = "run"
174tool = None
175tool_args = sys.argv[1:]
176for t in tools:
177 if name.endswith(f"-{t}"):
178 tool = t
179 break
180
181if not tool:
182 parser = argparse.ArgumentParser()
183 parser.add_argument('--arch', choices=['x86_64', 'aarch64'], required=True)
184 parser.add_argument('--ignore-cache', action='store_true')
185 group = parser.add_mutually_exclusive_group(required=True)
186 group.add_argument('--tool', choices=tools)
187 group.add_argument('--dump', action='store_true')
188 group.add_argument('--install', action='store_true')
189 parser.add_argument('args', nargs=argparse.REMAINDER)
190 args = parser.parse_args()
191 if args.dump:
192 action = "dump"
193 elif args.install:
194 action = "install"
195 arch = args.arch
196 tool = args.tool
197 ignore_cache = args.ignore_cache
198 tool_args = args.args
199
200if not arch:
201 if name.startswith('x86_64-'):
202 arch = 'x86_64'
203 elif name.startswith('aarch64-'):
204 arch = 'aarch64'
205 else:
206 print("unknown arch")
207 exit(1)
208
209config = get_config(arch, tool, ignore_cache)
210
211if action == "run":
212 tool_path = config['tools'][tool]
213 separator = ':' if tool == "midlrt.exe" else ';'
214 lib = separator.join(config['lib'])
215 include = separator.join(config['include'])
216 environ = dict(os.environ.copy(), LIB=lib, INCLUDE=include)
217 if tool == "midlrt.exe":
218 wslenv = environ['WSLENV']
219 if wslenv is None:
220 wslenv = ""
221 wslenv = wslenv + ":INCLUDE/wl:LIB/wl"
222 environ['WSLENV'] = wslenv
223
224 os.execvpe(tool_path, [tool_path] + tool_args, environ)
225elif action == "dump":
226 print(json.dumps(config))
227elif action == "install":
228 dir = os.path.dirname(__file__)
229 script = os.path.basename(__file__)
230 for tool in tools:
231 dst = f'{dir}/{arch}-{tool}'
232 if os.path.islink(dst):
233 os.unlink(dst)
234 os.symlink(script, dst)
235