code-intelligence.com/cifuzz@v0.40.0/internal/build/gradle/gradle.go (about) 1 package gradle 2 3 import ( 4 "io" 5 "os" 6 "os/exec" 7 "regexp" 8 "runtime" 9 "strings" 10 11 "github.com/pkg/errors" 12 13 "code-intelligence.com/cifuzz/internal/build" 14 "code-intelligence.com/cifuzz/internal/cmdutils" 15 "code-intelligence.com/cifuzz/internal/config" 16 "code-intelligence.com/cifuzz/pkg/log" 17 "code-intelligence.com/cifuzz/pkg/messaging" 18 "code-intelligence.com/cifuzz/pkg/runfiles" 19 "code-intelligence.com/cifuzz/util/fileutil" 20 ) 21 22 var ( 23 classpathRegex = regexp.MustCompile("(?m)^cifuzz.test.classpath=(?P<classpath>.*)$") 24 buildDirRegex = regexp.MustCompile("(?m)^cifuzz.buildDir=(?P<buildDir>.*)$") 25 ) 26 27 func FindGradleWrapper(projectDir string) (string, error) { 28 wrapper := "gradlew" 29 if runtime.GOOS == "windows" { 30 wrapper = "gradlew.bat" 31 } 32 33 return fileutil.SearchFileBackwards(projectDir, wrapper) 34 } 35 36 type ParallelOptions struct { 37 Enabled bool 38 NumJobs uint 39 } 40 41 type BuilderOptions struct { 42 ProjectDir string 43 Parallel ParallelOptions 44 Stdout io.Writer 45 Stderr io.Writer 46 } 47 48 func (opts *BuilderOptions) Validate() error { 49 // Check that the project dir is set 50 if opts.ProjectDir == "" { 51 return errors.New("ProjectDir is not set") 52 } 53 // Check that the project dir exists and can be accessed 54 _, err := os.Stat(opts.ProjectDir) 55 if err != nil { 56 return errors.WithStack(err) 57 } 58 return nil 59 } 60 61 type Builder struct { 62 *BuilderOptions 63 } 64 65 func NewBuilder(opts *BuilderOptions) (*Builder, error) { 66 err := opts.Validate() 67 if err != nil { 68 return nil, err 69 } 70 71 b := &Builder{BuilderOptions: opts} 72 73 return b, err 74 } 75 76 func (b *Builder) Build(targetClass string, targetMethod string) (*build.Result, error) { 77 gradleBuildLanguage, err := config.DetermineGradleBuildLanguage(b.ProjectDir) 78 if err != nil { 79 return nil, err 80 } 81 82 version, err := b.GradlePluginVersion() 83 if err != nil { 84 log.Error(errors.New("No cifuzz gradle plugin found")) 85 log.Print(messaging.Instructions(string(gradleBuildLanguage))) 86 return nil, cmdutils.WrapSilentError(err) 87 } 88 log.Debugf("Found gradle plugin version: %s", version) 89 90 deps, err := b.getDependencies() 91 if err != nil { 92 return nil, err 93 } 94 95 buildDir, err := GetBuildDirectory(b.ProjectDir) 96 if err != nil { 97 return nil, err 98 } 99 result := &build.Result{ 100 Name: targetClass, 101 TargetMethod: targetMethod, 102 BuildDir: buildDir, 103 ProjectDir: b.ProjectDir, 104 RuntimeDeps: deps, 105 } 106 107 return result, nil 108 } 109 110 func (b *Builder) GradlePluginVersion() (string, error) { 111 cmd, err := buildGradleCommand(b.ProjectDir, []string{"cifuzzPrintPluginVersion", "-q"}) 112 if err != nil { 113 return "", errors.WithStack(err) 114 } 115 log.Debugf("Command: %s", cmd.String()) 116 output, err := cmd.Output() 117 if err != nil { 118 return "", errors.WithStack(err) 119 } 120 121 return strings.TrimPrefix(string(output), "cifuzz.plugin.version="), nil 122 } 123 124 func (b *Builder) getDependencies() ([]string, error) { 125 cmd, err := buildGradleCommand(b.ProjectDir, []string{"cifuzzPrintTestClasspath", "-q"}) 126 if err != nil { 127 return nil, err 128 } 129 log.Debugf("Command: %s", cmd.String()) 130 output, err := cmd.Output() 131 if err != nil { 132 return nil, cmdutils.WrapExecError(errors.WithStack(err), cmd) 133 } 134 classpath := classpathRegex.FindStringSubmatch(string(output)) 135 deps := strings.Split(strings.TrimSpace(classpath[1]), string(os.PathListSeparator)) 136 137 return deps, nil 138 } 139 140 // GetGradleCommand returns the name of the gradle command. 141 // The gradle wrapper is preferred to use and gradle 142 // acts as a fallback command. 143 func GetGradleCommand(projectDir string) (string, error) { 144 wrapper, err := FindGradleWrapper(projectDir) 145 if err != nil && !errors.Is(err, os.ErrNotExist) { 146 return "", err 147 } 148 if wrapper != "" { 149 return wrapper, nil 150 } 151 152 gradleCmd, err := runfiles.Finder.GradlePath() 153 if err != nil { 154 return "", errors.WithStack(err) 155 } 156 return gradleCmd, nil 157 } 158 159 func buildGradleCommand(projectDir string, args []string) (*exec.Cmd, error) { 160 gradleCmd, err := GetGradleCommand(projectDir) 161 if err != nil { 162 return nil, err 163 } 164 165 cmd := exec.Command(gradleCmd, args...) 166 cmd.Dir = projectDir 167 168 return cmd, nil 169 } 170 171 func GetBuildDirectory(projectDir string) (string, error) { 172 cmd, err := buildGradleCommand(projectDir, []string{"cifuzzPrintBuildDir", "-q"}) 173 if err != nil { 174 return "", nil 175 } 176 177 log.Debugf("Command: %s", cmd.String()) 178 output, err := cmd.Output() 179 if err != nil { 180 return "", cmdutils.WrapExecError(errors.WithStack(err), cmd) 181 } 182 result := buildDirRegex.FindStringSubmatch(string(output)) 183 if result == nil { 184 return "", errors.New("Unable to parse gradle build directory from init script.") 185 } 186 buildDir := strings.TrimSpace(result[1]) 187 188 return buildDir, nil 189 } 190 191 func GetTestSourceSets(projectDir string) ([]string, error) { 192 cmd, err := buildGradleCommand(projectDir, []string{"cifuzzPrintTestSourceFolders", "-q"}) 193 if err != nil { 194 return nil, err 195 } 196 197 log.Debugf("Command: %s", cmd.String()) 198 output, err := cmd.Output() 199 if err != nil { 200 return nil, cmdutils.WrapExecError(errors.WithStack(err), cmd) 201 } 202 paths := strings.Split( 203 strings.TrimSpace( 204 strings.ReplaceAll(string(output), "cifuzz.test.source-folders=", ""), 205 ), 206 string(os.PathListSeparator)) 207 208 log.Debugf("found gradle test sources at: %s", paths) 209 return paths, nil 210 }