1# Copyright (C) 2023 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 16load("//build/bazel/rules/cc:cc_library_static.bzl", "cc_library_static") 17load("//build/bazel/rules/test_common:rules.bzl", "expect_failure_test") 18load(":cc_toolchain_config.bzl", "clang_version_info") 19 20def _clang_version_path_test_impl(ctx): 21 env = analysistest.begin(ctx) 22 actions = analysistest.target_actions(env) 23 24 compile_actions = [a for a in actions if a.mnemonic == "CppCompile"] 25 if len(compile_actions) != 1: 26 asserts.equals( 27 env, 28 1, 29 len(compile_actions), 30 "expected 1 compile action; found: " + str(compile_actions), 31 ) 32 return analysistest.end(env) 33 34 inputs = compile_actions[0].inputs.to_list() 35 clang_binary_files = [f for f in inputs if f.short_path.endswith("clang")] 36 if len(clang_binary_files) != 1: 37 asserts.equals( 38 env, 39 1, 40 len(clang_binary_files), 41 "expected 1 clang binary file; found: " + str(clang_binary_files), 42 ) 43 return analysistest.end(env) 44 clang_path = clang_binary_files[0] 45 asserts.equals( 46 env, 47 ctx.attr.expected_clang_path, 48 clang_path.path, 49 "expected clang binary path to be `%s`, but got `%s`" % ( 50 ctx.attr.expected_clang_path, 51 clang_path.path, 52 ), 53 ) 54 55 link_actions = [a for a in actions if a.mnemonic == "CppLink"] 56 if len(link_actions) != 1: 57 asserts.equals( 58 env, 59 1, 60 len(link_actions), 61 "expected 1 link action; found: " + str(link_actions), 62 ) 63 return analysistest.end(env) 64 inputs = link_actions[0].inputs.to_list() 65 66 libclang_rt_files = [f for f in inputs if "libclang_rt" in f.short_path] 67 if len(libclang_rt_files) != 1: 68 asserts.equals( 69 env, 70 1, 71 len(libclang_rt_files), 72 "expected 1 libclang_rt file; found: " + str(libclang_rt_files), 73 ) 74 return analysistest.end(env) 75 libclang_rt_path = libclang_rt_files[0] 76 asserts.equals( 77 env, 78 ctx.attr.expected_libclang_rt_path, 79 libclang_rt_path.path, 80 "expected libclang_rt path to be `%s`, but got `%s`" % ( 81 ctx.attr.expected_libclang_rt_path, 82 libclang_rt_path.path, 83 ), 84 ) 85 86 return analysistest.end(env) 87 88def _clang_version_path_test(clang_version, clang_short_version): 89 return analysistest.make( 90 _clang_version_path_test_impl, 91 attrs = { 92 "expected_clang_path": attr.string( 93 mandatory = True, 94 ), 95 "expected_libclang_rt_path": attr.string( 96 mandatory = True, 97 ), 98 }, 99 config_settings = { 100 "@//prebuilts/clang/host/linux-x86:clang_version": clang_version, 101 "@//prebuilts/clang/host/linux-x86:clang_short_version": clang_short_version, 102 }, 103 ) 104 105_clang_16_0_2_path_test = _clang_version_path_test("clang-r475365b", "16.0.2") 106 107def _test_clang_version_paths( 108 os, 109 arch, 110 clang_short_version, 111 clang_version_test, 112 expected_clang_path, 113 expected_libclang_rt_builtins_path): 114 name = "clang_version_paths_%s_%s_%s" % ( 115 os, 116 arch, 117 clang_short_version, 118 ) 119 test_name = name + "_test" 120 native.cc_binary( 121 name = name, 122 srcs = ["a.cpp"], 123 tags = ["manual"], 124 ) 125 clang_version_test( 126 name = test_name, 127 target_under_test = name, 128 expected_clang_path = expected_clang_path, 129 expected_libclang_rt_path = expected_libclang_rt_builtins_path, 130 target_compatible_with = [ 131 "//build/bazel_common_rules/platforms/os:" + os, 132 "//build/bazel_common_rules/platforms/arch:" + arch, 133 ], 134 ) 135 return test_name 136 137def _create_clang_version_tests(): 138 test_cases = [ 139 { 140 "os": "android", 141 "arch": "arm64", 142 "clang_short_version": "16.0.2", 143 "clang_version_test": _clang_16_0_2_path_test, 144 "expected_clang_path": "prebuilts/clang/host/linux-x86/clang-r475365b/bin/clang", 145 "expected_libclang_rt_builtins_path": "prebuilts/clang/host/linux-x86/clang-r475365b/lib/clang/16.0.2/lib/linux/libclang_rt.builtins-aarch64-android.a", 146 }, 147 ] 148 return [ 149 _test_clang_version_paths(**tc) 150 for tc in test_cases 151 ] 152 153def _filegroup_has_expected_files_test_impl(ctx): 154 env = analysistest.begin(ctx) 155 target_under_test = analysistest.target_under_test(env) 156 157 asserts.equals( 158 env, 159 ctx.attr.expected_files, 160 [f.short_path for f in target_under_test.files.to_list()], 161 ) 162 163 return analysistest.end(env) 164 165def _filegroup_has_expected_files_test(clang_version, clang_short_version): 166 return analysistest.make( 167 _filegroup_has_expected_files_test_impl, 168 attrs = { 169 "expected_files": attr.string_list( 170 mandatory = True, 171 ), 172 }, 173 config_settings = { 174 "@//prebuilts/clang/host/linux-x86:clang_version": clang_version, 175 "@//prebuilts/clang/host/linux-x86:clang_short_version": clang_short_version, 176 }, 177 ) 178 179_clang_16_0_2_filegroup_test = _filegroup_has_expected_files_test("clang-r475365b", "16.0.2") 180 181def _test_clang_version_filegroups( 182 os, 183 arch, 184 clang_short_version, 185 clang_version_filegroup_test, 186 expected_libclang_rt_builtins_path, 187 expected_libclang_rt_ubsan_minimal_path): 188 """ 189 These filegroup tests should give a clearer signal if the cc_toolchain 190 fails in analysis due to missing files from these filegroups. 191 """ 192 name = "clang_version_filegroups_%s_%s_%s" % ( 193 os, 194 arch, 195 clang_short_version, 196 ) 197 libclang_rt_builtins_test_name = name + "_test_libclang_rt_builtins" 198 libclang_rt_ubsan_minimal_test_name = name + "_test_libclang_rt_ubsan_minimal" 199 200 clang_version_filegroup_test( 201 name = libclang_rt_builtins_test_name, 202 target_under_test = "//prebuilts/clang/host/linux-x86:libclang_rt_builtins_%s_%s" % (os, arch), 203 expected_files = [expected_libclang_rt_builtins_path], 204 ) 205 clang_version_filegroup_test( 206 name = libclang_rt_ubsan_minimal_test_name, 207 target_under_test = "//prebuilts/clang/host/linux-x86:libclang_rt_ubsan_minimal_%s_%s" % (os, arch), 208 expected_files = [expected_libclang_rt_ubsan_minimal_path], 209 ) 210 211 return [ 212 libclang_rt_builtins_test_name, 213 libclang_rt_ubsan_minimal_test_name, 214 ] 215 216def _create_clang_version_filegroup_tests(): 217 test_cases = [ 218 { 219 "os": "android", 220 "arch": "arm64", 221 "clang_short_version": "16.0.2", 222 "clang_version_filegroup_test": _clang_16_0_2_filegroup_test, 223 "expected_libclang_rt_builtins_path": "prebuilts/clang/host/linux-x86/clang-r475365b/lib/clang/16.0.2/lib/linux/libclang_rt.builtins-aarch64-android.a", 224 "expected_libclang_rt_ubsan_minimal_path": "prebuilts/clang/host/linux-x86/clang-r475365b/lib/clang/16.0.2/lib/linux/libclang_rt.ubsan_minimal-aarch64-android.a", 225 }, 226 ] 227 return [ 228 t 229 for tc in test_cases 230 for t in _test_clang_version_filegroups(**tc) 231 ] 232 233def _test_clang_version_errors_for_missing_files(clang_version, clang_short_version): 234 name = "clang_version_errors_for_missing_files_%s-%s" % ( 235 clang_version, 236 clang_short_version, 237 ) 238 test_name = name + "_test" 239 240 clang_version_info( 241 name = name, 242 clang_files = native.glob(["**/*"]), 243 clang_short_version = clang_short_version, 244 clang_version = clang_version, 245 tags = ["manual"], 246 ) 247 expect_failure_test( 248 name = test_name, 249 target_under_test = name, 250 failure_message = "rule '//build/bazel/toolchains/clang/host/linux-x86:NOT-A-VERSION' does not exist", 251 ) 252 return test_name 253 254def _create_clang_version_missing_file_tests(): 255 test_cases = [ 256 { 257 "clang_version": "r487747", 258 "clang_short_version": "NOT-A-VERSION", 259 }, 260 { 261 "clang_version": "NOT-A-VERSION", 262 "clang_short_version": "16.0.2", 263 }, 264 ] 265 return [ 266 _test_clang_version_errors_for_missing_files(**tc) 267 for tc in test_cases 268 ] 269 270def cc_toolchain_clang_version_test_suite(name): 271 native.test_suite( 272 name = name, 273 tests = ( 274 _create_clang_version_tests() + 275 _create_clang_version_filegroup_tests() + 276 _create_clang_version_missing_file_tests() 277 ), 278 ) 279