1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
// +build go1.9
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/dirs"
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/modinfo"
"github.com/Azure/azure-sdk-for-go/eng/tools/profileBuilder/model"
"github.com/spf13/cobra"
)
const (
inputLongName = "input"
inputShortName = "i"
inputDescription = "Specify the input JSON file to read for the list of packages."
)
// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "Creates a profile from a set of packages.",
Long: `Reads a list of packages from stdin, where each line is treated as a Go package
identifier. These packages are then used to create a profile.
Often, the easiest way of invoking this command will be using a pipe operator
to specify the packages to include.
Example:
$> ../model/testdata/smallProfile.txt > profileBuilder list --name small_profile
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
logWriter := ioutil.Discard
if verboseFlag {
logWriter = os.Stdout
}
outputLog := log.New(logWriter, "[STATUS] ", 0)
errLog := log.New(os.Stderr, "[ERROR] ", 0)
if !filepath.IsAbs(outputRootDir) {
abs, err := filepath.Abs(outputRootDir)
if err != nil {
errLog.Fatalf("failed to convert to absolute path: %v", err)
}
outputRootDir = abs
}
inputFile, err := cmd.Flags().GetString(inputLongName)
if err != nil {
errLog.Fatalf("failed to get %s: %v", inputLongName, err)
}
data, err := ioutil.ReadFile(inputFile)
if err != nil {
errLog.Fatalf("failed to read list: %v", err)
}
var listDef model.ListDefinition
err = json.Unmarshal(data, &listDef)
if err != nil {
errLog.Fatalf("failed to unmarshal JSON: %v", err)
}
if modulesFlag {
// when generating in module-aware mode a few extra things need to happen
// 1. find the latest module version for the profile we're working on
// 2. update the list of includes package to their latest module versions
// 3. if any includes were updated generate a new major version for this profile
modver, err := getLatestModVer(outputRootDir)
if err != nil {
errLog.Fatalf("failed to get module dir: %v", err)
}
updated, err := updateModuleVersions(&listDef)
if err != nil {
errLog.Fatalf("failed to update module versions: %v", err)
}
if updated {
// at least one include was updated, write out updated list definition
data, err = json.MarshalIndent(listDef, "", " ")
if err != nil {
errLog.Fatalf("failed to marshal updated list: %v", err)
}
data = append(data, '\n')
err = ioutil.WriteFile(inputFile, data, 0666)
if err != nil {
errLog.Fatalf("failed to write updated list: %v", err)
}
// increment profile module major version and generate new go.mod file
modver = modinfo.IncrementModuleVersion(modver)
outputRootDir = filepath.Join(outputRootDir, modver)
err = generateGoMod(outputRootDir)
if err != nil {
errLog.Fatalf("failed to generate go.mod: %v", err)
}
} else if modver != "" {
outputRootDir = filepath.Join(outputRootDir, modver)
}
}
fmt.Printf("Executes profileBuilder in %s\n", outputRootDir)
outputLog.Printf("Output-Location set to: %s", outputRootDir)
if clearOutputFlag {
if err := clearOutputFolder(outputRootDir, listDef.IgnoredPaths); err != nil {
errLog.Fatalf("Unable to clear output-folder: %v", err)
}
}
// use recursive build to include the *api packages
model.BuildProfile(listDef, profileName, outputRootDir, outputLog, errLog, true, modulesFlag, semLimit)
},
}
func init() {
rootCmd.AddCommand(listCmd)
listCmd.Flags().StringP(inputLongName, inputShortName, "", inputDescription)
listCmd.MarkFlagRequired(inputLongName)
}
// for each included package check if there is a newer module major version then the one specified,
// and if there is update the include accordinly. returns true if any packages were updated.
func updateModuleVersions(listDef *model.ListDefinition) (bool, error) {
dirty := false
for i, entry := range listDef.Include {
// "../../services/storage/mgmt/2016-01-01/storage"
// "../../services/network/mgmt/2015-06-15/network/v2"
target := entry
if modinfo.HasVersionSuffix(target) {
target = filepath.Dir(target)
target = strings.Replace(target, "\\", "/", -1)
}
modDirs, err := modinfo.GetModuleSubdirs(target)
if err != nil {
return false, err
}
if len(modDirs) == 0 {
continue
}
latest := target + "/" + modDirs[len(modDirs)-1]
if latest == entry {
// already using latest major version
continue
}
listDef.Include[i] = latest
dirty = true
}
return dirty, nil
}
// returns the last major module version for the specified profile directory (e.g. "v2" etc).
// if there are no major versions the return value is the empty string.
func getLatestModVer(profileDir string) (string, error) {
subdirs, err := modinfo.GetModuleSubdirs(profileDir)
if err != nil {
return "", err
}
modDir := ""
if len(subdirs) > 0 {
modDir = subdirs[len(subdirs)-1]
}
return modDir, nil
}
// generate a go.mod file in the specified module major version directory (e.g. "profiles/foo/v2")
// the provided module directory is used to calculate the module name.
func generateGoMod(modDir string) error {
const gomodFormat = "module %s\n\ngo 1.12\n"
err := os.Mkdir(modDir, os.ModeDir|0644)
if err != nil {
return err
}
gomod, err := os.Create(filepath.Join(modDir, "go.mod"))
if err != nil {
return err
}
defer gomod.Close()
mod, err := modinfo.CreateModuleNameFromPath(modDir)
if err != nil {
return err
}
_, err = fmt.Fprintf(gomod, gomodFormat, mod)
return err
}
func clearOutputFolder(root string, excepts []string) error {
children, err := dirs.GetSubdirs(root)
if err != nil && !os.IsNotExist(err) {
return err
}
for _, child := range children {
if contains(excepts, child) {
continue
}
childPath := filepath.Join(root, child)
err = os.RemoveAll(childPath)
if err != nil {
return err
}
}
return nil
}
func contains(array []string, item string) bool {
for _, e := range array {
if e == item {
return true
}
}
return false
}
|