blob: f2eb4aee81700ec96d5dceb647515bafcc021a5d [file] [log] [blame]
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.bazel.rules.android.ndkcrosstools;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath;
import java.util.Arrays;
import java.util.List;
/**
* Class for creating paths that are specific to the structure of the Android NDK, but which are
* common to all crosstool toolchains.
*/
public class NdkPaths {
/**
* Removes the NDK repository prefix from the given path. Eg:
* "external/%repositoryName%/ndk/a/b/c" -> "ndk/a/b/c"
*/
public static String stripRepositoryPrefix(String path) {
return path.split("/", 3)[2];
}
private final String repositoryName;
private final String hostPlatform;
private final ApiLevel apiLevel;
private final Integer majorRevision;
public NdkPaths(
String repositoryName, String hostPlatform, ApiLevel apiLevel, Integer majorRevision) {
this.repositoryName = repositoryName;
this.hostPlatform = hostPlatform;
this.apiLevel = apiLevel;
this.majorRevision = majorRevision;
}
public ImmutableList<ToolPath> createToolpaths(String toolchainName, String targetPlatform,
CppConfiguration.Tool... excludedTools) {
ImmutableList.Builder<ToolPath> toolPaths = ImmutableList.builder();
for (Tool tool : CppConfiguration.Tool.values()) {
// Some toolchains don't have particular tools.
if (!Arrays.asList(excludedTools).contains(tool)) {
String toolPath = createToolPath(toolchainName, targetPlatform + "-" + tool.getNamePart());
toolPaths.add(ToolPath.newBuilder()
.setName(tool.getNamePart())
.setPath(toolPath)
.build());
}
}
return toolPaths.build();
}
public ImmutableList<ToolPath> createClangToolpaths(String toolchainName, String targetPlatform,
String llvmVersion, CppConfiguration.Tool... excludedTools) {
// Add GCC to the list of excluded tools. It will be replaced by clang below.
excludedTools = ImmutableList.<CppConfiguration.Tool>builder()
.add(excludedTools)
.add(CppConfiguration.Tool.GCC)
.build()
.toArray(new CppConfiguration.Tool[excludedTools.length + 1]);
// Create the regular tool paths, then add clang.
return ImmutableList.<ToolPath>builder()
.addAll(createToolpaths(toolchainName, targetPlatform, excludedTools))
.add(ToolPath.newBuilder()
.setName("gcc")
.setPath(createToolPath(llvmVersion == null ? "llvm" : "llvm-" + llvmVersion, "clang"))
.build())
.build();
}
private String createToolPath(String toolchainName, String toolName) {
String toolpathTemplate = "ndk/toolchains/%toolchainName%/prebuilt/%hostPlatform%"
+ "/bin/%toolName%";
return toolpathTemplate
.replace("%repositoryName%", repositoryName)
.replace("%toolchainName%", toolchainName)
.replace("%hostPlatform%", hostPlatform)
.replace("%toolName%", toolName);
}
public static String getToolchainDirectoryFromToolPath(String toolPath) {
return toolPath.split("/")[2];
}
public String createGccToolchainPath(String toolchainName) {
String gccToolchainPathTemplate =
"external/%repositoryName%/ndk/toolchains/%toolchainName%/prebuilt/%hostPlatform%";
return gccToolchainPathTemplate
.replace("%repositoryName%", repositoryName)
.replace("%toolchainName%", toolchainName)
.replace("%hostPlatform%", hostPlatform);
}
/**
* Gets the clang NDK builtin includes directories that exist in the NDK. These directories are
* always searched for header files by clang and should be added to the CROSSTOOL in the
* cxx_builtin_include_directories list.
*
* <p>You can see the list of directories and the order that they are searched in by running
* {@code clang -E -x c++ - -v < /dev/null}.
*/
public String createClangToolchainBuiltinIncludeDirectory(String clangVersion) {
String clangBuiltinIncludeDirectoryPathTemplate =
"external/%repositoryName%/ndk/toolchains/llvm/prebuilt/%hostPlatform%/lib64/clang/"
+ "%clangVersion%/include";
return clangBuiltinIncludeDirectoryPathTemplate
.replace("%repositoryName%", repositoryName)
.replace("%hostPlatform%", hostPlatform)
.replace("%clangVersion%", clangVersion);
}
/**
* Gets the gcc NDK builtin includes directories that exist in the NDK. These directories are
* always searched for header files by clang and should be added to the CROSSTOOL in the
* cxx_builtin_include_directories list.
*
* <p>You can see the list of directories and the order that they are searched in by running
* {@code gcc -E -x c++ - -v < /dev/null}.
*/
public List<String> createGccToolchainBuiltinIncludeDirectories(
final String toolchainName, final String targetPlatform, final String gccVersion) {
final String toolchainIncludePathTemplate =
"external/%repositoryName%/ndk/toolchains/%toolchainName%/prebuilt/%hostPlatform%"
+ "/lib/gcc/%targetPlatform%/%gccVersion%/%includeFolderName%";
return Lists.transform(
ImmutableList.of("include", "include-fixed"),
includeFolderName ->
toolchainIncludePathTemplate
.replace("%repositoryName%", repositoryName)
.replace("%toolchainName%", toolchainName)
.replace("%hostPlatform%", hostPlatform)
.replace("%targetPlatform%", targetPlatform)
.replace("%gccVersion%", gccVersion)
.replace("%includeFolderName%", includeFolderName));
}
/**
* NDK 14 and below. Each API level has its own headers. See
* https://android.googlesource.com/platform/ndk/+/ndk-r15-release/docs/UnifiedHeaders.md#supporting-unified-headers-in-your-build-system
*
* @param targetCpu the target CPU architecture
* @return the path to the compile time sysroot
*/
public String createBuiltinSysroot(String targetCpu) {
String correctedApiLevel = apiLevel.getCpuCorrectedApiLevel(targetCpu);
String androidPlatformIncludePathTemplate =
"external/%repositoryName%/ndk/platforms/android-%apiLevel%/arch-%arch%";
return androidPlatformIncludePathTemplate
.replace("%repositoryName%", repositoryName)
.replace("%apiLevel%", correctedApiLevel)
.replace("%arch%", targetCpu);
}
/**
* NDK 15 and above. The headers have been unified into ndk/sysroot.
*
* @return the sysroot location for NDK 15 and above.
*/
public String createBuiltinSysroot() {
// This location does not exist prior to NDK 15
Preconditions.checkState(majorRevision >= 15);
return "external/%repositoryName%/ndk/sysroot".replace("%repositoryName%", repositoryName);
}
public String getCorrectedApiLevel(String targetCpu) {
return apiLevel.getCpuCorrectedApiLevel(targetCpu);
}
ImmutableList<String> createGnuLibstdcIncludePaths(String gccVersion, String targetCpu) {
String cpuNoThumb = targetCpu.replaceAll("-thumb$", "");
String prefix = "external/%repositoryName%/ndk/sources/cxx-stl/gnu-libstdc++/%gccVersion%/";
List<String> includePathTemplates = Arrays.asList(
prefix + "include",
prefix + "libs/%targetCpu%/include",
prefix + "include/backward");
ImmutableList.Builder<String> includePaths = ImmutableList.builder();
for (String template : includePathTemplates) {
includePaths.add(
template
.replace("%repositoryName%", repositoryName)
.replace("%gccVersion%", gccVersion)
.replace("%targetCpu%", cpuNoThumb));
}
return includePaths.build();
}
ImmutableList<String> createStlportIncludePaths() {
String prefix =
"external/%repositoryName%/ndk/sources/cxx-stl/"
.replace("%repositoryName%", repositoryName);
return ImmutableList.of(prefix + "stlport/stlport", prefix + "gabi++/include");
}
ImmutableList<String> createLibcxxIncludePaths() {
String prefix =
"external/%repositoryName%/ndk/sources/".replace("%repositoryName%", repositoryName);
ImmutableList.Builder<String> includePaths = ImmutableList.builder();
if (majorRevision <= 12) {
includePaths.add(prefix + "cxx-stl/llvm-libc++/libcxx/include");
includePaths.add(prefix + "cxx-stl/llvm-libc++abi/libcxxabi/include");
} else {
// libcxx/include was moved one level up from r13 onwards.
// See https://github.com/bazelbuild/bazel/issues/3641
includePaths.add(prefix + "cxx-stl/llvm-libc++/include");
includePaths.add(prefix + "cxx-stl/llvm-libc++abi/include");
}
includePaths.add(prefix + "android/support/include");
return includePaths.build();
}
/**
* @param stl The STL name as it appears in the NDK path
* @param gccVersion The GCC version "4.8" or "4.9", applicable only to gnu-libstdc++, or null
* @param targetCpu Target CPU
* @param fileExtension "a" or "so"
* @return A glob pattern for the STL runtime libs in the NDK.
*/
static String createStlRuntimeLibsGlob(
String stl, String gccVersion, String targetCpu, String fileExtension) {
if (gccVersion != null) {
stl += "/" + gccVersion;
}
targetCpu = targetCpu.replaceAll("-thumb$", "/thumb");
String template =
"ndk/sources/cxx-stl/%stl%/libs/%targetCpu%/*.%fileExtension%";
return template
.replace("%stl%", stl)
.replace("%targetCpu%", targetCpu)
.replace("%fileExtension%", fileExtension);
}
/**
* @param targetCpu Target CPU
* @return the directory of the target CPU's runtime .a files for linking
*/
public String createLibcppLinkerPath(String targetCpu) {
return "external/%repositoryName%/ndk/sources/cxx-stl/llvm-libc++/libs/%targetCpu%"
.replace("%repositoryName%", repositoryName)
.replace("%targetCpu%", targetCpu);
}
}