/* * Copyright 2016-present Facebook, Inc. * * 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.facebook.buck.cxx; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.RuleKeyAppendable; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import java.nio.file.Path; import java.util.Optional; /** Encapsulates headers from a single root location. */ public abstract class CxxHeaders implements RuleKeyAppendable { public abstract CxxPreprocessables.IncludeType getIncludeType(); /** @return the root of the includes. */ public abstract SourcePath getRoot(); /** @return the path to the optional header map to use for this header pack. */ public abstract Optional<SourcePath> getHeaderMap(); /** * @return the path to add to the preprocessor search path to find the includes. This defaults to * the root, but can be overridden to use an alternate path. */ public abstract SourcePath getIncludeRoot(); /** * Add this header pack to the given {@link com.facebook.buck.cxx.HeaderPathNormalizer.Builder}. */ public abstract void addToHeaderPathNormalizer(HeaderPathNormalizer.Builder builder); /** @return all deps required by this header pack. */ public abstract Iterable<BuildRule> getDeps(SourcePathRuleFinder ruleFinder); private static Path resolveSourcePathAndShorten( SourcePathResolver resolver, SourcePath path, Optional<PathShortener> pathShortener) { Path resolvedPath = resolver.getAbsolutePath(path); return pathShortener.isPresent() ? pathShortener.get().shorten(resolvedPath) : resolvedPath; } /** * @return the arguments to add to the preprocessor command line to include the given header packs * in preprocessor search path. */ public static Iterable<String> getArgs( Iterable<CxxHeaders> cxxHeaderses, SourcePathResolver resolver, Optional<PathShortener> pathMinimizer, Preprocessor preprocessor) { ImmutableList.Builder<String> args = ImmutableList.builder(); // Collect the header maps and roots into buckets organized by include type, so that we can: // 1) Apply the header maps first (so that they work properly). // 2) De-duplicate redundant include paths. Multimap<CxxPreprocessables.IncludeType, String> headerMaps = LinkedHashMultimap.create(); Multimap<CxxPreprocessables.IncludeType, String> roots = LinkedHashMultimap.create(); for (CxxHeaders cxxHeaders : cxxHeaderses) { Optional<SourcePath> headerMap = cxxHeaders.getHeaderMap(); if (headerMap.isPresent()) { headerMaps.put( cxxHeaders.getIncludeType(), resolveSourcePathAndShorten(resolver, headerMap.get(), pathMinimizer).toString()); } roots.put( cxxHeaders.getIncludeType(), resolveSourcePathAndShorten(resolver, cxxHeaders.getIncludeRoot(), pathMinimizer) .toString()); } // Define the include type ordering. We always add local ("-I") include paths first so that // headers match there before system ("-isystem") ones. ImmutableSet<CxxPreprocessables.IncludeType> includeTypes = ImmutableSet.of( CxxPreprocessables.IncludeType.LOCAL, CxxPreprocessables.IncludeType.SYSTEM); // Apply the header maps first, so that headers that matching there avoid falling back to // stat'ing files in the normal include roots. Preconditions.checkState(includeTypes.containsAll(headerMaps.keySet())); for (CxxPreprocessables.IncludeType includeType : includeTypes) { args.addAll(includeType.includeArgs(preprocessor, headerMaps.get(includeType))); } // Apply the regular includes last. Preconditions.checkState(includeTypes.containsAll(roots.keySet())); for (CxxPreprocessables.IncludeType includeType : includeTypes) { args.addAll(includeType.includeArgs(preprocessor, roots.get(includeType))); } return args.build(); } }