@@ -9,9 +9,11 @@ import (
99 "log/slog"
1010 "os"
1111 "path/filepath"
12+ "strings"
1213
1314 "github.com/open-telemetry/opentelemetry-go-compile-instrumentation/tool/ex"
1415 "github.com/open-telemetry/opentelemetry-go-compile-instrumentation/tool/util"
16+ "golang.org/x/tools/go/packages"
1517)
1618
1719type SetupPhase struct {
@@ -39,6 +41,90 @@ func isSetup() bool {
3941 return false
4042}
4143
44+ // flagsWithPathValues contains flags that accept a value from "go build" command.
45+ //
46+ //nolint:gochecknoglobals // private lookup table
47+ var flagsWithPathValues = map [string ]bool {
48+ "-C" : true ,
49+ "-o" : true ,
50+ "-p" : true ,
51+ "-covermode" : true ,
52+ "-coverpkg" : true ,
53+ "-asmflags" : true ,
54+ "-buildmode" : true ,
55+ "-buildvcs" : true ,
56+ "-compiler" : true ,
57+ "-gccgoflags" : true ,
58+ "-gcflags" : true ,
59+ "-installsuffix" : true ,
60+ "-ldflags" : true ,
61+ "-mod" : true ,
62+ "-modfile" : true ,
63+ "-overlay" : true ,
64+ "-pgo" : true ,
65+ "-pkgdir" : true ,
66+ "-tags" : true ,
67+ "-toolexec" : true ,
68+ }
69+
70+ // GetBuildPackages loads all packages from the go build command arguments.
71+ // Returns a list of loaded packages. If no package patterns are found in args,
72+ // defaults to loading the current directory package.
73+ // The args parameter should be the go build command arguments (e.g., ["build", "-a", "./cmd"]).
74+ // Returns an error if package loading fails or if invalid patterns are provided.
75+ // For example:
76+ // - args ["build", "-a", "./cmd"] returns packages for "./cmd"
77+ // - args ["build", "-a", "cmd"] returns packages for the "cmd" package in the module
78+ // - args ["build", "-a", ".", "./cmd"] returns packages for both "." and "./cmd"
79+ // - args ["build"] returns packages for "."
80+ func getBuildPackages (ctx context.Context , args []string ) ([]* packages.Package , error ) {
81+ logger := util .LoggerFromContext (ctx )
82+
83+ buildPkgs := make ([]* packages.Package , 0 )
84+ cfg := & packages.Config {
85+ Mode : packages .NeedName | packages .NeedFiles | packages .NeedModule ,
86+ }
87+ found := false
88+ for i := len (args ) - 1 ; i >= 0 ; i -- {
89+ arg := args [i ]
90+
91+ // If preceded by a flag that takes a path value, this is a flag value
92+ // We want to avoid scenarios like "go build -o ./tmp ./app" where tmp also contains Go files,
93+ // as it would be treated as a package.
94+ if i > 0 && flagsWithPathValues [args [i - 1 ]] {
95+ break
96+ }
97+
98+ // If we hit a flag, stop. Packages come after all flags
99+ // go build [-o output] [build flags] [packages]
100+ if strings .HasPrefix (arg , "-" ) || arg == "go" || arg == "build" || arg == "install" {
101+ break
102+ }
103+
104+ pkgs , err := packages .Load (cfg , arg )
105+ if err != nil {
106+ return nil , ex .Wrapf (err , "failed to load packages for pattern %s" , arg )
107+ }
108+ for _ , pkg := range pkgs {
109+ if pkg .Errors != nil || pkg .Module == nil {
110+ logger .DebugContext (ctx , "skipping package" , "pattern" , arg , "errors" , pkg .Errors , "module" , pkg .Module )
111+ continue
112+ }
113+ buildPkgs = append (buildPkgs , pkg )
114+ found = true
115+ }
116+ }
117+
118+ if ! found {
119+ var err error
120+ buildPkgs , err = packages .Load (cfg , "." )
121+ if err != nil {
122+ return nil , ex .Wrapf (err , "failed to load packages for pattern ." )
123+ }
124+ }
125+ return buildPkgs , nil
126+ }
127+
42128// Setup prepares the environment for further instrumentation.
43129func Setup (ctx context.Context , args []string ) error {
44130 logger := util .LoggerFromContext (ctx )
@@ -51,6 +137,14 @@ func Setup(ctx context.Context, args []string) error {
51137 sp := & SetupPhase {
52138 logger : logger ,
53139 }
140+
141+ // Introduce additional hook code by generating otel.runtime.go
142+ // Use GetPackage to determine the build target directory
143+ pkgs , err := getBuildPackages (ctx , args )
144+ if err != nil {
145+ return err
146+ }
147+
54148 // Find all dependencies of the project being build
55149 deps , err := sp .findDeps (ctx , args )
56150 if err != nil {
@@ -61,10 +155,11 @@ func Setup(ctx context.Context, args []string) error {
61155 if err != nil {
62156 return err
63157 }
64- // Introduce additional hook code by generating otel.instrumentation.go
65- err = sp .addDeps (matched )
66- if err != nil {
67- return err
158+ for _ , pkg := range pkgs {
159+ // Introduce additional hook code by generating otel.runtime.go
160+ if err = sp .addDeps (matched , pkg .Dir ); err != nil {
161+ return err
162+ }
68163 }
69164 // Extract the embedded instrumentation modules into local directory
70165 err = sp .extract ()
@@ -77,11 +172,7 @@ func Setup(ctx context.Context, args []string) error {
77172 return err
78173 }
79174 // Write the matched hook to matched.txt for further instrument phase
80- err = sp .store (matched )
81- if err != nil {
82- return err
83- }
84- return nil
175+ return sp .store (matched )
85176}
86177
87178// BuildWithToolexec builds the project with the toolexec mode
@@ -128,16 +219,21 @@ func GoBuild(ctx context.Context, args []string) error {
128219 logger .DebugContext (ctx , "failed to back up files" , "error" , err )
129220 }
130221 defer func () {
131- err = os .RemoveAll (OtelRuntimeFile )
222+ var pkgs []* packages.Package
223+ pkgs , err = getBuildPackages (ctx , args )
132224 if err != nil {
133- logger .DebugContext (ctx , "failed to remove otel runtime file " , "error" , err )
225+ logger .DebugContext (ctx , "failed to get build packages " , "error" , err )
134226 }
135- err = os .RemoveAll (unzippedPkgDir )
136- if err != nil {
227+ for _ , pkg := range pkgs {
228+ if err = os .RemoveAll (filepath .Join (pkg .Dir , OtelRuntimeFile )); err != nil {
229+ logger .DebugContext (ctx , "failed to remove generated file from package" ,
230+ "file" , filepath .Join (pkg .Dir , OtelRuntimeFile ), "error" , err )
231+ }
232+ }
233+ if err = os .RemoveAll (unzippedPkgDir ); err != nil {
137234 logger .DebugContext (ctx , "failed to remove unzipped pkg" , "error" , err )
138235 }
139- err = util .RestoreFile (backupFiles )
140- if err != nil {
236+ if err = util .RestoreFile (backupFiles ); err != nil {
141237 logger .DebugContext (ctx , "failed to restore files" , "error" , err )
142238 }
143239 }()
0 commit comments