microsoft/openvmm

Public

mirrored from https://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
release/2505-fork

Branches

Tags

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

Clone

HTTPS

Download ZIP

build_support/windows_cross/cross_tool.py

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