github.com/ajguerrer/rules_go@v0.20.3/go/tools/builders/asm.go (about)

     1  // Copyright 2017 The Bazel Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"go/build"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strconv"
    26  	"strings"
    27  )
    28  
    29  // asm builds a single .s file with "go tool asm". It is invoked by the
    30  // Go rules as an action.
    31  func asm(args []string) error {
    32  	// Parse arguments.
    33  	args, err := readParamsFiles(args)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	builderArgs, asmFlags := splitArgs(args)
    38  	var outPath string
    39  	flags := flag.NewFlagSet("GoAsm", flag.ExitOnError)
    40  	flags.StringVar(&outPath, "o", "", "The output archive file to write")
    41  	goenv := envFlags(flags)
    42  	if err := flags.Parse(builderArgs); err != nil {
    43  		return err
    44  	}
    45  	if err := goenv.checkFlags(); err != nil {
    46  		return err
    47  	}
    48  	if flags.NArg() != 1 {
    49  		return fmt.Errorf("wanted exactly 1 source file; got %d", flags.NArg())
    50  	}
    51  	source := flags.Args()[0]
    52  
    53  	// Filter the input file.
    54  	metadata, err := readFileInfo(build.Default, source, false)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	if !metadata.matched {
    59  		source = os.DevNull
    60  	}
    61  
    62  	// Build source with the assembler.
    63  	return asmFile(goenv, source, asmFlags, outPath)
    64  }
    65  
    66  // buildSymabisFile generates a file from assembly files that is consumed
    67  // by the compiler. This is only needed in go1.12+ when there is at least one
    68  // .s file. If the symabis file is not needed, no file will be generated,
    69  // and "", nil will be returned.
    70  func buildSymabisFile(goenv *env, sFiles, hFiles []fileInfo, asmhdr string) (string, error) {
    71  	if len(sFiles) == 0 {
    72  		return "", nil
    73  	}
    74  
    75  	// Check version. The symabis file is only required and can only be built
    76  	// starting at go1.12.
    77  	version := runtime.Version()
    78  	if strings.HasPrefix(version, "go1.") {
    79  		minor := version[len("go1."):]
    80  		if i := strings.IndexByte(minor, '.'); i >= 0 {
    81  			minor = minor[:i]
    82  		}
    83  		n, err := strconv.Atoi(minor)
    84  		if err == nil && n <= 11 {
    85  			return "", nil
    86  		}
    87  		// Fall through if the version can't be parsed. It's probably a newer
    88  		// development version.
    89  	}
    90  
    91  	// Create an empty go_asm.h file. The compiler will write this later, but
    92  	// we need one to exist now.
    93  	asmhdrFile, err := os.Create(asmhdr)
    94  	if err != nil {
    95  		return "", err
    96  	}
    97  	if err := asmhdrFile.Close(); err != nil {
    98  		return "", err
    99  	}
   100  	asmhdrDir := filepath.Dir(asmhdr)
   101  
   102  	// Create a temporary output file. The caller is responsible for deleting it.
   103  	var symabisName string
   104  	symabisFile, err := ioutil.TempFile("", "symabis")
   105  	if err != nil {
   106  		return "", err
   107  	}
   108  	symabisName = symabisFile.Name()
   109  	symabisFile.Close()
   110  
   111  	// Run the assembler.
   112  	wd, err := os.Getwd()
   113  	if err != nil {
   114  		return symabisName, err
   115  	}
   116  	asmargs := goenv.goTool("asm")
   117  	asmargs = append(asmargs, "-trimpath", wd)
   118  	asmargs = append(asmargs, "-I", wd)
   119  	asmargs = append(asmargs, "-I", filepath.Join(os.Getenv("GOROOT"), "pkg", "include"))
   120  	asmargs = append(asmargs, "-I", asmhdrDir)
   121  	seenHdrDirs := map[string]bool{wd: true, asmhdrDir: true}
   122  	for _, hFile := range hFiles {
   123  		hdrDir := filepath.Dir(abs(hFile.filename))
   124  		if !seenHdrDirs[hdrDir] {
   125  			asmargs = append(asmargs, "-I", hdrDir)
   126  			seenHdrDirs[hdrDir] = true
   127  		}
   128  	}
   129  	// TODO(#1894): define GOOS_goos, GOARCH_goarch, both here and in the
   130  	// GoAsm action.
   131  	asmargs = append(asmargs, "-gensymabis", "-o", symabisName, "--")
   132  	for _, sFile := range sFiles {
   133  		asmargs = append(asmargs, sFile.filename)
   134  	}
   135  
   136  	err = goenv.runCommand(asmargs)
   137  	return symabisName, err
   138  }
   139  
   140  func asmFile(goenv *env, srcPath string, asmFlags []string, outPath string) error {
   141  	args := goenv.goTool("asm")
   142  	args = append(args, asmFlags...)
   143  	args = append(args, "-trimpath", ".")
   144  	args = append(args, "-o", outPath)
   145  	args = append(args, "--", srcPath)
   146  	absArgs(args, []string{"-I", "-o", "-trimpath"})
   147  	return goenv.runCommand(args)
   148  }