cmd/dotnet/sdk/main.go (82 lines of code) (raw):
// Copyright 2020 Google LLC
//
// 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.
// Binary dotnet/runtime buildpack detects .NET applications
// and install the corresponding version of .NET runtime.
package main
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/buildpacks/pkg/dotnet"
"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
"github.com/GoogleCloudPlatform/buildpacks/pkg/runtime"
"github.com/buildpacks/libcnb/v2"
)
const (
sdkLayerName = "sdk"
devModeKey = "devmode"
)
func main() {
gcp.Main(detectFn, buildFn)
}
func detectFn(ctx *gcp.Context) (gcp.DetectResult, error) {
if result := runtime.CheckOverride("dotnet"); result != nil {
return result, nil
}
files, err := dotnet.ProjectFiles(ctx, ".")
if err != nil {
return nil, err
}
if len(files) != 0 {
return gcp.OptIn("found project files: " + strings.Join(files, ", ")), nil
}
return gcp.OptOut("no project files or .dll files found"), nil
}
func buildFn(ctx *gcp.Context) error {
sdkVersion, err := dotnet.GetSDKVersion(ctx)
if err != nil {
return err
}
isDevMode, err := env.IsDevMode()
if err != nil {
return fmt.Errorf("checking if dev mode is enabled: %w", err)
}
if err := buildSDKLayer(ctx, sdkVersion, isDevMode); err != nil {
return fmt.Errorf("building the sdk layer: %w", err)
}
return nil
}
func buildSDKLayer(ctx *gcp.Context, version string, isDevMode bool) error {
// Keep the SDK layer for launch in devmode because we use `dotnet watch`.
sdkl, err := ctx.Layer(sdkLayerName, gcp.BuildLayer, gcp.CacheLayer, gcp.LaunchLayerIfDevMode)
if err != nil {
return fmt.Errorf("creating %v layer: %w", sdkLayerName, err)
}
if strconv.FormatBool(isDevMode) != ctx.GetMetadata(sdkl, devModeKey) {
if err := ctx.ClearLayer(sdkl); err != nil {
return fmt.Errorf("clearing layer %q: %w", sdkl.Name, err)
}
}
if _, err := runtime.InstallTarballIfNotCached(ctx, runtime.DotnetSDK, version, sdkl); err != nil {
return err
}
setSDKEnvVars(ctx, sdkl, isDevMode)
ctx.SetMetadata(sdkl, devModeKey, strconv.FormatBool(isDevMode))
return nil
}
func setSDKEnvVars(ctx *gcp.Context, sdkl *libcnb.Layer, isDevMode bool) {
if dotnet.RequiresGlobalizationInvariant(ctx) {
sdkl.BuildEnvironment.Default("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1")
}
if isDevMode {
setSDKEnvVarsDevMode(sdkl)
} else {
setSDKEnvVarsForBuild(sdkl)
}
}
// setSDKEnvVarsDevMode sets the env vars for dev mode. In dev mode, the full
// SDK is present at launch time and the runtime layer is not created.
func setSDKEnvVarsDevMode(sdkl *libcnb.Layer) {
sdkl.SharedEnvironment.Default("DOTNET_ROOT", sdkl.Path)
sdkl.SharedEnvironment.Prepend("PATH", string(os.PathListSeparator), sdkl.Path)
sdkl.LaunchEnvironment.Default("DOTNET_RUNNING_IN_CONTAINER", "true")
}
// setSDKEnvVarsForBuild sets the SDK variables needed at build time. The SDK
// layer is only present for the build and the runtime layer is present in the launch
// image.
func setSDKEnvVarsForBuild(sdkl *libcnb.Layer) {
sdkl.BuildEnvironment.Default("DOTNET_ROOT", sdkl.Path)
sdkl.BuildEnvironment.Prepend("PATH", string(os.PathListSeparator), sdkl.Path)
}