blob: 2c9936382a19c28179782a7007ad21919856f3d5 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001"""Triples are a way to define information about a platform/system. This module provides
2a way to convert a triple string into a well structured object to avoid constant string
Brian Silverman5f6f2762022-08-13 19:30:05 -07003parsing in starlark code, and a way for a repository_rule to extract the target triple
4of the host platform.
Brian Silvermancc09f182022-03-09 15:40:20 -08005
Adam Snaider1c095c92023-07-08 02:09:58 -04006Triples can be described at the following link:
Brian Silvermancc09f182022-03-09 15:40:20 -08007https://clang.llvm.org/docs/CrossCompilation.html#target-triple
8"""
9
10def triple(triple):
11 """Constructs a struct containing each component of the provided triple
12
13 Args:
14 triple (str): A platform triple. eg: `x86_64-unknown-linux-gnu`
15
16 Returns:
17 struct:
18 - arch (str): The triple's CPU architecture
19 - vendor (str): The vendor of the system
20 - system (str): The name of the system
21 - abi (str, optional): The abi to use or None if abi does not apply.
Brian Silverman5f6f2762022-08-13 19:30:05 -070022 - str (str): Original string representation of the triple
Brian Silvermancc09f182022-03-09 15:40:20 -080023 """
24 if triple == "wasm32-wasi":
25 return struct(
26 arch = "wasm32",
27 system = "wasi",
28 vendor = "wasi",
29 abi = None,
Brian Silverman5f6f2762022-08-13 19:30:05 -070030 str = triple,
Brian Silvermancc09f182022-03-09 15:40:20 -080031 )
Adam Snaider1c095c92023-07-08 02:09:58 -040032 elif triple in ("aarch64-fuchsia", "x86_64-fuchsia"):
33 return struct(
34 arch = triple.split("-")[0],
35 system = "fuchsia",
36 vendor = "fuchsia",
37 abi = None,
38 str = triple,
39 )
Brian Silvermancc09f182022-03-09 15:40:20 -080040
41 component_parts = triple.split("-")
42 if len(component_parts) < 3:
43 fail("Expected target triple to contain at least three sections separated by '-'")
44
45 cpu_arch = component_parts[0]
46 vendor = component_parts[1]
47 system = component_parts[2]
48 abi = None
49
Adam Snaider1c095c92023-07-08 02:09:58 -040050 if cpu_arch.startswith(("thumbv8m", "thumbv7m", "thumbv7e", "thumbv6m")):
51 abi = system
52 system = vendor
53 vendor = None
54
Brian Silvermancc09f182022-03-09 15:40:20 -080055 if system == "androideabi":
56 system = "android"
57 abi = "eabi"
58
59 if len(component_parts) == 4:
60 abi = component_parts[3]
61
62 return struct(
63 arch = cpu_arch,
64 vendor = vendor,
65 system = system,
66 abi = abi,
Brian Silverman5f6f2762022-08-13 19:30:05 -070067 str = triple,
Brian Silvermancc09f182022-03-09 15:40:20 -080068 )
Brian Silverman5f6f2762022-08-13 19:30:05 -070069
70_CPU_ARCH_ERROR_MSG = """\
71Command failed with exit code '{code}': {args}
72----------stdout:
73{stdout}
74----------stderr:
75{stderr}
76"""
77
78def _query_cpu_architecture(repository_ctx, expected_archs, is_windows = False):
79 """Detect the host CPU architecture
80
81 Args:
82 repository_ctx (repository_ctx): The repository_rule's context object
83 expected_archs (list): A list of expected architecture strings
84 is_windows (bool, optional): If true, the cpu lookup will use the windows method (`wmic` vs `uname`)
85
86 Returns:
87 str: The host's CPU architecture
88 """
89 if is_windows:
90 arguments = ["wmic", "os", "get", "osarchitecture"]
91 else:
92 arguments = ["uname", "-m"]
93
94 result = repository_ctx.execute(arguments)
95
96 if result.return_code:
97 fail(_CPU_ARCH_ERROR_MSG.format(
98 code = result.return_code,
99 args = arguments,
100 stdout = result.stdout,
101 stderr = result.stderr,
102 ))
103
104 if is_windows:
105 # Example output:
106 # OSArchitecture
107 # 64-bit
Adam Snaider1c095c92023-07-08 02:09:58 -0400108 #
109 # In some cases windows can return the same but with an uppercase b
110 # OSArchitecture
111 # 64-Bit
Brian Silverman5f6f2762022-08-13 19:30:05 -0700112 lines = result.stdout.split("\n")
Adam Snaider1c095c92023-07-08 02:09:58 -0400113 arch = lines[1].strip().lower()
Brian Silverman5f6f2762022-08-13 19:30:05 -0700114
115 # Translate 64-bit to a compatible rust platform
116 # https://doc.rust-lang.org/nightly/rustc/platform-support.html
Adam Snaider1c095c92023-07-08 02:09:58 -0400117 if arch.startswith("arm 64-bit"):
118 arch = "aarch64"
119 elif arch == "64-bit":
Brian Silverman5f6f2762022-08-13 19:30:05 -0700120 arch = "x86_64"
121 else:
122 arch = result.stdout.strip("\n")
123
124 # Correct the arm architecture for macos
125 if "mac" in repository_ctx.os.name and arch == "arm64":
126 arch = "aarch64"
127
128 if not arch in expected_archs:
129 fail("{} is not a expected cpu architecture {}\n{}".format(
130 arch,
131 expected_archs,
132 result.stdout,
133 ))
134
135 return arch
136
137def get_host_triple(repository_ctx, abi = None):
138 """Query host information for the appropriate triple to use with load_arbitrary_tool or the crate_universe resolver
139
140 Example:
141
142 ```python
143 load("@rules_rust//rust:repositories.bzl", "load_arbitrary_tool")
144 load("@rules_rust//rust/platform:triple.bzl", "get_host_triple")
145
146 def _impl(repository_ctx):
147 host_triple = get_host_triple(repository_ctx)
148
149 load_arbitrary_tool(
150 ctx = repository_ctx,
151 tool_name = "cargo",
152 tool_subdirectories = ["cargo"],
153 target_triple = host_triple.str,
154 )
155
156 example = repository_rule(implementation = _impl)
157 ```
158
159 Args:
160 repository_ctx (repository_ctx): The repository_rule's context object
161 abi (str): Since there's no consistent way to check for ABI, this info
162 may be explicitly provided
163
164 Returns:
165 struct: A triple struct; see the `triple` function in this module
166 """
167
168 # Detect the host's cpu architecture
169
170 supported_architectures = {
171 "linux": ["aarch64", "x86_64"],
172 "macos": ["aarch64", "x86_64"],
Adam Snaider1c095c92023-07-08 02:09:58 -0400173 "windows": ["aarch64", "x86_64"],
Brian Silverman5f6f2762022-08-13 19:30:05 -0700174 }
175
176 if "linux" in repository_ctx.os.name:
177 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["linux"])
178 return triple("{}-unknown-linux-{}".format(
179 cpu,
180 abi or "gnu",
181 ))
182
183 if "mac" in repository_ctx.os.name:
184 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["macos"])
185 return triple("{}-apple-darwin".format(cpu))
186
187 if "win" in repository_ctx.os.name:
188 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["windows"], True)
189 return triple("{}-pc-windows-{}".format(
190 cpu,
191 abi or "msvc",
192 ))
193
194 fail("Unhandled host os: {}", repository_ctx.os.name)