/* * Copyright 2013 the original author or authors. * * 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 org.gradle.language.nativeplatform.internal.incremental; import com.google.common.collect.Sets; import org.gradle.internal.FileUtils; import org.gradle.language.nativeplatform.internal.Include; import org.gradle.language.nativeplatform.internal.IncludeDirectives; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Set; public class DefaultSourceIncludesResolver implements SourceIncludesResolver { private final List<File> includePaths; public DefaultSourceIncludesResolver(List<File> includePaths) { this.includePaths = includePaths; } @Override public ResolvedSourceIncludes resolveIncludes(File sourceFile, IncludeDirectives includes) { BuildableResolvedSourceIncludes resolvedSourceIncludes = new BuildableResolvedSourceIncludes(); searchForDependencies(prependSourceDir(sourceFile, includePaths), includes.getQuotedIncludes(), resolvedSourceIncludes); searchForDependencies(includePaths, includes.getSystemIncludes(), resolvedSourceIncludes); if (!includes.getMacroIncludes().isEmpty()) { resolvedSourceIncludes.resolved(includes.getMacroIncludes().get(0).getValue(), null); } return resolvedSourceIncludes; } private List<File> prependSourceDir(File sourceFile, List<File> includePaths) { List<File> quotedSearchPath = new ArrayList<File>(includePaths.size() + 1); quotedSearchPath.add(sourceFile.getParentFile()); quotedSearchPath.addAll(includePaths); return quotedSearchPath; } private void searchForDependencies(List<File> searchPath, List<Include> includes, BuildableResolvedSourceIncludes dependencies) { for (Include include : includes) { searchForDependency(searchPath, include.getValue(), dependencies); } } private void searchForDependency(List<File> searchPath, String include, BuildableResolvedSourceIncludes dependencies) { for (File searchDir : searchPath) { File candidate = new File(searchDir, include); // TODO: SLG This isn't correct, we need to consider directories too // If a source file is #include <type_trait> // and includePath = [ A, B ] // and /B/type_trait is the header we want. // We need /A/type_trait to be recorded as a directory in case it becomes a file later. if (!candidate.isDirectory()) { dependencies.searched(candidate); } if (candidate.isFile()) { dependencies.resolved(include, candidate); return; } } } private static class BuildableResolvedSourceIncludes implements ResolvedSourceIncludes { private final Set<ResolvedInclude> dependencies = Sets.newLinkedHashSet(); private final Set<File> candidates = Sets.newLinkedHashSet(); void searched(File candidate) { candidates.add(candidate); } void resolved(String rawInclude, File resolved) { File dependencyFile = resolved == null ? null : FileUtils.canonicalize(resolved); dependencies.add(new ResolvedInclude(rawInclude, dependencyFile)); } @Override public Set<ResolvedInclude> getResolvedIncludes() { return dependencies; } @Override public Set<File> getCheckedLocations() { return candidates; } } }