Home | History | Annotate | Download | only in soong_ui
      1 // Copyright 2017 Google Inc. 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 	"context"
     19 	"flag"
     20 	"fmt"
     21 	"os"
     22 	"path/filepath"
     23 	"strconv"
     24 	"strings"
     25 	"time"
     26 
     27 	"android/soong/ui/build"
     28 	"android/soong/ui/logger"
     29 	"android/soong/ui/tracer"
     30 )
     31 
     32 func indexList(s string, list []string) int {
     33 	for i, l := range list {
     34 		if l == s {
     35 			return i
     36 		}
     37 	}
     38 
     39 	return -1
     40 }
     41 
     42 func inList(s string, list []string) bool {
     43 	return indexList(s, list) != -1
     44 }
     45 
     46 func main() {
     47 	log := logger.New(os.Stderr)
     48 	defer log.Cleanup()
     49 
     50 	if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
     51 		os.Args[1] == "--dumpvars-mode" ||
     52 		os.Args[1] == "--dumpvar-mode") {
     53 
     54 		log.Fatalln("The `soong` native UI is not yet available.")
     55 	}
     56 
     57 	ctx, cancel := context.WithCancel(context.Background())
     58 	defer cancel()
     59 
     60 	trace := tracer.New(log)
     61 	defer trace.Close()
     62 
     63 	build.SetupSignals(log, cancel, func() {
     64 		trace.Close()
     65 		log.Cleanup()
     66 	})
     67 
     68 	buildCtx := build.Context{&build.ContextImpl{
     69 		Context:        ctx,
     70 		Logger:         log,
     71 		Tracer:         trace,
     72 		StdioInterface: build.StdioImpl{},
     73 	}}
     74 	var config build.Config
     75 	if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
     76 		config = build.NewConfig(buildCtx)
     77 	} else {
     78 		config = build.NewConfig(buildCtx, os.Args[1:]...)
     79 	}
     80 
     81 	log.SetVerbose(config.IsVerbose())
     82 	build.SetupOutDir(buildCtx, config)
     83 
     84 	if config.Dist() {
     85 		logsDir := filepath.Join(config.DistDir(), "logs")
     86 		os.MkdirAll(logsDir, 0777)
     87 		log.SetOutput(filepath.Join(logsDir, "soong.log"))
     88 		trace.SetOutput(filepath.Join(logsDir, "build.trace"))
     89 	} else {
     90 		log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
     91 		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
     92 	}
     93 
     94 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
     95 		if !strings.HasSuffix(start, "N") {
     96 			if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
     97 				log.Verbosef("Took %dms to start up.",
     98 					time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
     99 				buildCtx.CompleteTrace("startup", start_time, uint64(time.Now().UnixNano()))
    100 			}
    101 		}
    102 
    103 		if executable, err := os.Executable(); err == nil {
    104 			trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
    105 		}
    106 	}
    107 
    108 	f := build.NewSourceFinder(buildCtx, config)
    109 	defer f.Shutdown()
    110 	build.FindSources(buildCtx, config, f)
    111 
    112 	if os.Args[1] == "--dumpvar-mode" {
    113 		dumpVar(buildCtx, config, os.Args[2:])
    114 	} else if os.Args[1] == "--dumpvars-mode" {
    115 		dumpVars(buildCtx, config, os.Args[2:])
    116 	} else {
    117 		toBuild := build.BuildAll
    118 		if config.Checkbuild() {
    119 			toBuild |= build.RunBuildTests
    120 		}
    121 		build.Build(buildCtx, config, toBuild)
    122 	}
    123 }
    124 
    125 func dumpVar(ctx build.Context, config build.Config, args []string) {
    126 	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
    127 	flags.Usage = func() {
    128 		fmt.Fprintf(os.Stderr, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
    129 		fmt.Fprintln(os.Stderr, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
    130 		fmt.Fprintln(os.Stderr, "")
    131 
    132 		fmt.Fprintln(os.Stderr, "'report_config' is a special case that prints the human-readable config banner")
    133 		fmt.Fprintln(os.Stderr, "from the beginning of the build.")
    134 		fmt.Fprintln(os.Stderr, "")
    135 		flags.PrintDefaults()
    136 	}
    137 	abs := flags.Bool("abs", false, "Print the absolute path of the value")
    138 	flags.Parse(args)
    139 
    140 	if flags.NArg() != 1 {
    141 		flags.Usage()
    142 		os.Exit(1)
    143 	}
    144 
    145 	varName := flags.Arg(0)
    146 	if varName == "report_config" {
    147 		varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
    148 		if err != nil {
    149 			ctx.Fatal(err)
    150 		}
    151 
    152 		fmt.Println(build.Banner(varData))
    153 	} else {
    154 		varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
    155 		if err != nil {
    156 			ctx.Fatal(err)
    157 		}
    158 
    159 		if *abs {
    160 			var res []string
    161 			for _, path := range strings.Fields(varData[varName]) {
    162 				if abs, err := filepath.Abs(path); err == nil {
    163 					res = append(res, abs)
    164 				} else {
    165 					ctx.Fatalln("Failed to get absolute path of", path, err)
    166 				}
    167 			}
    168 			fmt.Println(strings.Join(res, " "))
    169 		} else {
    170 			fmt.Println(varData[varName])
    171 		}
    172 	}
    173 }
    174 
    175 func dumpVars(ctx build.Context, config build.Config, args []string) {
    176 	flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
    177 	flags.Usage = func() {
    178 		fmt.Fprintf(os.Stderr, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
    179 		fmt.Fprintln(os.Stderr, "In dumpvars mode, dump the values of one or more legacy make variables, in")
    180 		fmt.Fprintln(os.Stderr, "shell syntax. The resulting output may be sourced directly into a shell to")
    181 		fmt.Fprintln(os.Stderr, "set corresponding shell variables.")
    182 		fmt.Fprintln(os.Stderr, "")
    183 
    184 		fmt.Fprintln(os.Stderr, "'report_config' is a special case that dumps a variable containing the")
    185 		fmt.Fprintln(os.Stderr, "human-readable config banner from the beginning of the build.")
    186 		fmt.Fprintln(os.Stderr, "")
    187 		flags.PrintDefaults()
    188 	}
    189 
    190 	varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
    191 	absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
    192 
    193 	varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
    194 	absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
    195 
    196 	flags.Parse(args)
    197 
    198 	if flags.NArg() != 0 {
    199 		flags.Usage()
    200 		os.Exit(1)
    201 	}
    202 
    203 	vars := strings.Fields(*varsStr)
    204 	absVars := strings.Fields(*absVarsStr)
    205 
    206 	allVars := append([]string{}, vars...)
    207 	allVars = append(allVars, absVars...)
    208 
    209 	if i := indexList("report_config", allVars); i != -1 {
    210 		allVars = append(allVars[:i], allVars[i+1:]...)
    211 		allVars = append(allVars, build.BannerVars...)
    212 	}
    213 
    214 	if len(allVars) == 0 {
    215 		return
    216 	}
    217 
    218 	varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
    219 	if err != nil {
    220 		ctx.Fatal(err)
    221 	}
    222 
    223 	for _, name := range vars {
    224 		if name == "report_config" {
    225 			fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
    226 		} else {
    227 			fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
    228 		}
    229 	}
    230 	for _, name := range absVars {
    231 		var res []string
    232 		for _, path := range strings.Fields(varData[name]) {
    233 			abs, err := filepath.Abs(path)
    234 			if err != nil {
    235 				ctx.Fatalln("Failed to get absolute path of", path, err)
    236 			}
    237 			res = append(res, abs)
    238 		}
    239 		fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
    240 	}
    241 }
    242