blob: 2c42704084985ade6f3007e503f51cff4baa0728 [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
6Triples can be described at the following link:
7https://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 )
32
33 component_parts = triple.split("-")
34 if len(component_parts) < 3:
35 fail("Expected target triple to contain at least three sections separated by '-'")
36
37 cpu_arch = component_parts[0]
38 vendor = component_parts[1]
39 system = component_parts[2]
40 abi = None
41
42 if system == "androideabi":
43 system = "android"
44 abi = "eabi"
45
46 if len(component_parts) == 4:
47 abi = component_parts[3]
48
49 return struct(
50 arch = cpu_arch,
51 vendor = vendor,
52 system = system,
53 abi = abi,
Brian Silverman5f6f2762022-08-13 19:30:05 -070054 str = triple,
Brian Silvermancc09f182022-03-09 15:40:20 -080055 )
Brian Silverman5f6f2762022-08-13 19:30:05 -070056
57_CPU_ARCH_ERROR_MSG = """\
58Command failed with exit code '{code}': {args}
59----------stdout:
60{stdout}
61----------stderr:
62{stderr}
63"""
64
65def _query_cpu_architecture(repository_ctx, expected_archs, is_windows = False):
66 """Detect the host CPU architecture
67
68 Args:
69 repository_ctx (repository_ctx): The repository_rule's context object
70 expected_archs (list): A list of expected architecture strings
71 is_windows (bool, optional): If true, the cpu lookup will use the windows method (`wmic` vs `uname`)
72
73 Returns:
74 str: The host's CPU architecture
75 """
76 if is_windows:
77 arguments = ["wmic", "os", "get", "osarchitecture"]
78 else:
79 arguments = ["uname", "-m"]
80
81 result = repository_ctx.execute(arguments)
82
83 if result.return_code:
84 fail(_CPU_ARCH_ERROR_MSG.format(
85 code = result.return_code,
86 args = arguments,
87 stdout = result.stdout,
88 stderr = result.stderr,
89 ))
90
91 if is_windows:
92 # Example output:
93 # OSArchitecture
94 # 64-bit
95 lines = result.stdout.split("\n")
96 arch = lines[1].strip()
97
98 # Translate 64-bit to a compatible rust platform
99 # https://doc.rust-lang.org/nightly/rustc/platform-support.html
100 if arch == "64-bit":
101 arch = "x86_64"
102 else:
103 arch = result.stdout.strip("\n")
104
105 # Correct the arm architecture for macos
106 if "mac" in repository_ctx.os.name and arch == "arm64":
107 arch = "aarch64"
108
109 if not arch in expected_archs:
110 fail("{} is not a expected cpu architecture {}\n{}".format(
111 arch,
112 expected_archs,
113 result.stdout,
114 ))
115
116 return arch
117
118def get_host_triple(repository_ctx, abi = None):
119 """Query host information for the appropriate triple to use with load_arbitrary_tool or the crate_universe resolver
120
121 Example:
122
123 ```python
124 load("@rules_rust//rust:repositories.bzl", "load_arbitrary_tool")
125 load("@rules_rust//rust/platform:triple.bzl", "get_host_triple")
126
127 def _impl(repository_ctx):
128 host_triple = get_host_triple(repository_ctx)
129
130 load_arbitrary_tool(
131 ctx = repository_ctx,
132 tool_name = "cargo",
133 tool_subdirectories = ["cargo"],
134 target_triple = host_triple.str,
135 )
136
137 example = repository_rule(implementation = _impl)
138 ```
139
140 Args:
141 repository_ctx (repository_ctx): The repository_rule's context object
142 abi (str): Since there's no consistent way to check for ABI, this info
143 may be explicitly provided
144
145 Returns:
146 struct: A triple struct; see the `triple` function in this module
147 """
148
149 # Detect the host's cpu architecture
150
151 supported_architectures = {
152 "linux": ["aarch64", "x86_64"],
153 "macos": ["aarch64", "x86_64"],
154 "windows": ["x86_64"],
155 }
156
157 if "linux" in repository_ctx.os.name:
158 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["linux"])
159 return triple("{}-unknown-linux-{}".format(
160 cpu,
161 abi or "gnu",
162 ))
163
164 if "mac" in repository_ctx.os.name:
165 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["macos"])
166 return triple("{}-apple-darwin".format(cpu))
167
168 if "win" in repository_ctx.os.name:
169 cpu = _query_cpu_architecture(repository_ctx, supported_architectures["windows"], True)
170 return triple("{}-pc-windows-{}".format(
171 cpu,
172 abi or "msvc",
173 ))
174
175 fail("Unhandled host os: {}", repository_ctx.os.name)