/* * Copyright 2014-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.ocaml; import com.facebook.buck.rules.AbstractBuildRule; import com.facebook.buck.rules.AddToRuleKey; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.ExplicitBuildTargetSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.step.Step; import com.facebook.buck.step.fs.MkdirStep; import com.facebook.buck.util.MoreCollectors; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.nio.file.Path; import java.util.Optional; public class OcamlLink extends AbstractBuildRule { @AddToRuleKey private final ImmutableList<SourcePath> inputs; @AddToRuleKey private final ImmutableMap<String, String> cxxCompilerEnvironment; @AddToRuleKey private final ImmutableList<String> cxxCompiler; @AddToRuleKey private final Tool ocamlCompiler; @AddToRuleKey private final ImmutableList<Arg> flags; @AddToRuleKey private final Optional<String> stdlib; @AddToRuleKey(stringify = true) private final Path outputRelativePath; @AddToRuleKey(stringify = true) private final Path outputNativePluginPath; @AddToRuleKey private final ImmutableList<Arg> depInput; @AddToRuleKey private final ImmutableList<Arg> cDepInput; @AddToRuleKey private final boolean isLibrary; @AddToRuleKey private final boolean isBytecode; @AddToRuleKey private final boolean buildNativePlugin; public OcamlLink( BuildRuleParams params, ImmutableList<SourcePath> inputs, ImmutableMap<String, String> cxxCompilerEnvironment, ImmutableList<String> cxxCompiler, Tool ocamlCompiler, ImmutableList<Arg> flags, Optional<String> stdlib, Path outputRelativePath, Path outputNativePluginPath, ImmutableList<Arg> depInput, ImmutableList<Arg> cDepInput, boolean isLibrary, boolean isBytecode, boolean buildNativePlugin) { super(params); this.inputs = inputs; this.cxxCompilerEnvironment = cxxCompilerEnvironment; this.cxxCompiler = cxxCompiler; this.ocamlCompiler = ocamlCompiler; this.flags = flags; this.stdlib = stdlib; this.outputRelativePath = outputRelativePath; this.outputNativePluginPath = outputNativePluginPath; this.depInput = depInput; this.cDepInput = cDepInput; this.isLibrary = isLibrary; this.isBytecode = isBytecode; this.buildNativePlugin = buildNativePlugin; } @Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { for (Path artifact : getAllOutputs()) { buildableContext.recordArtifact(artifact); } ImmutableList.Builder<Step> steps = ImmutableList.<Step>builder() .add(MkdirStep.of(getProjectFilesystem(), outputRelativePath.getParent())) .add( OcamlLinkStep.create( getProjectFilesystem().getRootPath(), cxxCompilerEnvironment, cxxCompiler, ocamlCompiler.getCommandPrefix(context.getSourcePathResolver()), flags, stdlib, getProjectFilesystem().resolve(outputRelativePath), depInput, cDepInput, inputs .stream() .map(context.getSourcePathResolver()::getAbsolutePath) .collect(MoreCollectors.toImmutableList()), isLibrary, isBytecode, context.getSourcePathResolver())); if (isLibrary && buildNativePlugin) { ImmutableList.Builder<String> ocamlInputBuilder = ImmutableList.builder(); final String linkExt = OcamlCompilables.OCAML_CMXS; for (String linkInput : Arg.stringify(depInput, context.getSourcePathResolver())) { if (linkInput.endsWith(linkExt)) { ocamlInputBuilder.add(linkInput); } } ImmutableList<String> ocamlInput = ocamlInputBuilder.build(); steps.add( new OcamlNativePluginStep( getProjectFilesystem().getRootPath(), cxxCompilerEnvironment, cxxCompiler, ocamlCompiler.getCommandPrefix(context.getSourcePathResolver()), Arg.stringify(flags, context.getSourcePathResolver()), stdlib, getProjectFilesystem().resolve(outputNativePluginPath), cDepInput, inputs .stream() .map(context.getSourcePathResolver()::getAbsolutePath) .collect(MoreCollectors.toImmutableList()), ocamlInput)); } return steps.build(); } private ImmutableSet<Path> getAllOutputs() { if (isLibrary && !isBytecode) { return OcamlUtil.getExtensionVariants( outputRelativePath, OcamlCompilables.OCAML_A, OcamlCompilables.OCAML_CMXA); } else { return ImmutableSet.of(outputRelativePath); } } @Override public SourcePath getSourcePathToOutput() { return new ExplicitBuildTargetSourcePath(getBuildTarget(), outputRelativePath); } @Override public boolean isCacheable() { // TODO(10456582): when some libraries are fetched from cache and others are built locally // The digest of the implementations may not match up, and ocaml throws a fit. // In lieu of tracking that down, forcing libraries to not cache will ensure that all libraries // will rely on identical object files. return false; } }