/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.c;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.ImageSingletons;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.option.OptionUtils;
import com.oracle.svm.core.util.VMError;
public final class ProjectHeaderFile {
@AutomaticFeature
public static class RegisterSVMTestingResolverFeature extends RegisterFallbackResolverFeature {
@Override
public boolean isInConfiguration(IsInConfigurationAccess access) {
return access.findClassByName("com.oracle.svm.tutorial.CInterfaceTutorial") != null;
}
@Override
public void afterRegistration(AfterRegistrationAccess access) {
/**
* Search for headers in a directory, relative to the current working directory, that
* contains the Substrate VM projects. Using the "../substratevm*" relative path
* accounts for running SVM from sibling suites.
*/
HeaderResolversRegistry.registerAdditionalResolver(new FallbackHeaderResolver("../../graal/substratevm/src"));
}
}
@AutomaticFeature
public static class HeaderResolverRegistrationFeature implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(HeaderResolversRegistry.class, new HeaderResolversRegistry());
}
}
Base class for fall back resolvers registration. Extending this class will ensure that the ProjectHeaderFile
will be added as a dependency. /**
* Base class for fall back resolvers registration. Extending this class will ensure that the
* {@link ProjectHeaderFile} will be added as a dependency.
*/
public abstract static class RegisterFallbackResolverFeature implements Feature {
@Override
public List<Class<? extends Feature>> getRequiredFeatures() {
return Collections.singletonList(HeaderResolverRegistrationFeature.class);
}
}
Resolves the path to a C header file that is located in a Substrate VM project.
/**
* Resolves the path to a C header file that is located in a Substrate VM project.
*/
public static String resolve(String projectName, String headerFile) {
HeaderResolversRegistry resolvers = ImageSingletons.lookup(HeaderResolversRegistry.class);
return resolvers.resolve(projectName, headerFile);
}
A registry for all the header resolvers. The search order is important, we want first to
search the location(s) specified by CLibraryPath, then registered fall back locations if any.
/**
* A registry for all the header resolvers. The search order is important, we want first to
* search the location(s) specified by CLibraryPath, then registered fall back locations if any.
*/
public static class HeaderResolversRegistry {
Register additional resolvers. /** Register additional resolvers. */
public static void registerAdditionalResolver(HeaderResolver resolver) {
assert ImageSingletons.contains(HeaderResolversRegistry.class);
HeaderResolversRegistry registry = ImageSingletons.lookup(HeaderResolversRegistry.class);
registry.register(resolver);
}
private MainHeaderResolver mainResolver;
private List<HeaderResolver> fallbackResolvers;
public HeaderResolversRegistry() {
mainResolver = new MainHeaderResolver();
fallbackResolvers = new ArrayList<>();
}
private void register(HeaderResolver resolver) {
fallbackResolvers.add(resolver);
}
public String resolve(String projectName, String headerFile) {
/* First search using the main resolver. */
HeaderSearchResult mainResult = mainResolver.resolveHeader(projectName, headerFile);
if (mainResult.header.isPresent()) {
return mainResult.header.get();
}
/* Then search using the fallback resolvers, if any. */
List<String> fallbackLocations = new ArrayList<>();
for (HeaderResolver resolver : fallbackResolvers) {
HeaderSearchResult result = resolver.resolveHeader(projectName, headerFile);
fallbackLocations.addAll(result.locations);
if (result.header.isPresent()) {
return result.header.get();
}
}
/* If the header was not found at any of the specified locations an error is thrown. */
throw VMError.shouldNotReachHere("Header file " + headerFile +
" not found at main search location(s): \n" + String.join("\n", mainResult.locations) +
(fallbackLocations.size() > 0 ? "\n or any of the fallback locations: \n" + String.join("\n", fallbackLocations) : "") +
"\n Use option -H:CLibraryPath to specify header file search locations.");
}
}
Used for resolving header files. /** Used for resolving header files. */
public interface HeaderResolver {
Tries to resolve a header given the project name and the header file name. /** Tries to resolve a header given the project name and the header file name. */
HeaderSearchResult resolveHeader(String projectName, String headerFile);
}
Contains the search result and the locations searched. /** Contains the search result and the locations searched. */
public static class HeaderSearchResult {
The header file, if found. /** The header file, if found. */
Optional<String> header;
The locations where the this resolver searched for headers. /** The locations where the this resolver searched for headers. */
protected List<String> locations;
public HeaderSearchResult(Optional<String> headerFile, List<String> locations) {
this.header = headerFile;
this.locations = locations;
}
public HeaderSearchResult(Optional<String> headerFile, String... locations) {
this.header = headerFile;
this.locations = Arrays.asList(locations);
}
}
Header resolver based on CLibraryPath. /** Header resolver based on CLibraryPath. */
static class MainHeaderResolver implements HeaderResolver {
@Override
public HeaderSearchResult resolveHeader(String projectName, String headerFile) {
List<String> locations = new ArrayList<>();
for (String clibPathComponent : OptionUtils.flatten(",", SubstrateOptions.CLibraryPath.getValue())) {
Path clibPathHeaderFile = Paths.get(clibPathComponent).resolve(headerFile).normalize().toAbsolutePath();
locations.add(clibPathHeaderFile.toString());
if (Files.exists(clibPathHeaderFile)) {
return new HeaderSearchResult(Optional.of("\"" + clibPathHeaderFile + "\""), locations);
}
}
return new HeaderSearchResult(Optional.empty(), locations);
}
}
This kind of resolving is for SubstrateVM internal use (to run our regression tests). /** This kind of resolving is for SubstrateVM internal use (to run our regression tests). */
public static class FallbackHeaderResolver implements HeaderResolver {
final String projectsDir;
public FallbackHeaderResolver(String projectsDir) {
this.projectsDir = projectsDir;
}
@Override
public HeaderSearchResult resolveHeader(String projectName, String headerFile) {
Path fallbackHeaderFile = Paths.get(projectsDir).resolve(projectName).resolve(headerFile).normalize().toAbsolutePath();
if (Files.exists(fallbackHeaderFile)) {
return new HeaderSearchResult(Optional.of("\"" + fallbackHeaderFile + "\""), fallbackHeaderFile.toString());
}
return new HeaderSearchResult(Optional.empty(), fallbackHeaderFile.toString());
}
}
}