From e673480115152dced469708dfbdcebf0beeeb1d8 Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Fri, 1 Nov 2024 19:10:48 +0300 Subject: [PATCH 1/3] fix for k2 build --- internal/client/includes-collector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/client/includes-collector.go b/internal/client/includes-collector.go index d68b148..7ebe167 100644 --- a/internal/client/includes-collector.go +++ b/internal/client/includes-collector.go @@ -121,7 +121,7 @@ func CollectDependentIncludesByCxxM(includesCache *IncludesCache, cwd string, cx // This is done by -Wp,-v option for a no input file. // This result is cached once nocc-daemon is started. func GetDefaultCxxIncludeDirsOnLocal(cxxName string) (IncludeDirs, error) { - cxxWpCommand := exec.Command(cxxName, "-Wp,-v", "-x", "c++", "/dev/null", "-fsyntax-only") + cxxWpCommand := exec.Command(cxxName, "-Wp,-v", "-x", "c++", "/dev/null", "-fsyntax-only", "-stdlib=libc++") var cxxWpStderr bytes.Buffer cxxWpCommand.Stderr = &cxxWpStderr if err := cxxWpCommand.Run(); err != nil { From df394e24df033f9eac2e437c91870977eefee10b Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Thu, 6 Feb 2025 19:38:56 +0300 Subject: [PATCH 2/3] mvp version --- internal/client/daemon.go | 11 ++++--- internal/client/includes-cache.go | 4 +-- internal/client/includes-collector.go | 8 ++++-- internal/client/invocation.go | 41 ++++++++++++++++++--------- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/internal/client/daemon.go b/internal/client/daemon.go index b60a4a2..7b6450f 100644 --- a/internal/client/daemon.go +++ b/internal/client/daemon.go @@ -11,6 +11,7 @@ import ( "os/user" "path/filepath" "runtime" + "strings" "sync" "syscall" "time" @@ -274,15 +275,17 @@ func (daemon *Daemon) FallbackToLocalCxx(req DaemonSockRequest, reason error) Da return reply } -func (daemon *Daemon) GetOrCreateIncludesCache(cxxName string) *IncludesCache { +func (daemon *Daemon) GetOrCreateIncludesCache(cxxName string, cxxArgs []string) *IncludesCache { + cxxCacheName := cxxName + strings.Join(cxxArgs, " ") + daemon.mu.Lock() - includesCache := daemon.includesCache[cxxName] + includesCache := daemon.includesCache[cxxCacheName] if includesCache == nil { var err error - if includesCache, err = MakeIncludesCache(cxxName); err != nil { + if includesCache, err = MakeIncludesCache(cxxName, cxxArgs); err != nil { logClient.Error("failed to calc default include dirs for", cxxName, err) } - daemon.includesCache[cxxName] = includesCache + daemon.includesCache[cxxCacheName] = includesCache } daemon.mu.Unlock() return includesCache diff --git a/internal/client/includes-cache.go b/internal/client/includes-cache.go index da5d3ba..9d27717 100644 --- a/internal/client/includes-cache.go +++ b/internal/client/includes-cache.go @@ -27,8 +27,8 @@ type IncludesCache struct { mu sync.RWMutex } -func MakeIncludesCache(cxxName string) (*IncludesCache, error) { - cxxDefIDirs, err := GetDefaultCxxIncludeDirsOnLocal(cxxName) +func MakeIncludesCache(cxxName string, cxxArgs []string) (*IncludesCache, error) { + cxxDefIDirs, err := GetDefaultCxxIncludeDirsOnLocal(cxxName, cxxArgs) return &IncludesCache{ cxxName: cxxName, diff --git a/internal/client/includes-collector.go b/internal/client/includes-collector.go index 7ebe167..dd7d5ed 100644 --- a/internal/client/includes-collector.go +++ b/internal/client/includes-collector.go @@ -120,8 +120,12 @@ func CollectDependentIncludesByCxxM(includesCache *IncludesCache, cwd string, cx // GetDefaultCxxIncludeDirsOnLocal retrieves default include dirs on a local machine. // This is done by -Wp,-v option for a no input file. // This result is cached once nocc-daemon is started. -func GetDefaultCxxIncludeDirsOnLocal(cxxName string) (IncludeDirs, error) { - cxxWpCommand := exec.Command(cxxName, "-Wp,-v", "-x", "c++", "/dev/null", "-fsyntax-only", "-stdlib=libc++") +func GetDefaultCxxIncludeDirsOnLocal(cxxName string, cxxArgs []string) (IncludeDirs, error) { + cxxCmdLine := make([]string, 0, len(cxxArgs)+5) + cxxCmdLine = append(cxxCmdLine, cxxArgs...) + cxxCmdLine = append(cxxCmdLine, "-Wp,-v", "-x", "c++", "/dev/null", "-fsyntax-only") + + cxxWpCommand := exec.Command(cxxName, cxxCmdLine...) var cxxWpStderr bytes.Buffer cxxWpCommand.Stderr = &cxxWpStderr if err := cxxWpCommand.Run(); err != nil { diff --git a/internal/client/invocation.go b/internal/client/invocation.go index 1893ba6..fd257da 100644 --- a/internal/client/invocation.go +++ b/internal/client/invocation.go @@ -27,12 +27,13 @@ type Invocation struct { sessionID uint32 // incremental while a daemon is alive // cmdLine is parsed to the following fields: - cppInFile string // input file as specified in cmd line (.cpp for compilation, .h for pch generation) - objOutFile string // output file as specified in cmd line (.o for compilation, .gch/.pch for pch generation) - cxxName string // g++ / clang / etc. - cxxArgs []string // args like -Wall, -fpch-preprocess and many more, except: - cxxIDirs IncludeDirs // -I / -iquote / -isystem go here - depsFlags DepCmdFlags // -MD -MF file and others, used for .d files generation (not passed to server) + cppInFile string // input file as specified in cmd line (.cpp for compilation, .h for pch generation) + objOutFile string // output file as specified in cmd line (.o for compilation, .gch/.pch for pch generation) + cxxName string // g++ / clang / etc. + cxxArgs []string // args like -Wall, -fpch-preprocess and many more, except: + cxxArgsIncludes []string // TODO + cxxIDirs IncludeDirs // -I / -iquote / -isystem go here + depsFlags DepCmdFlags // -MD -MF file and others, used for .d files generation (not passed to server) waitUploads int32 // files still waiting for upload to finish; 0 releases wgUpload; see Invocation.DoneUploadFile doneRecv int32 // 1 if o file received or failed receiving; 1 releases wgRecv; see Invocation.DoneRecvObj @@ -73,13 +74,14 @@ func pathAbs(cwd string, relPath string) string { func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invocation *Invocation) { invocation = &Invocation{ - createTime: time.Now(), - sessionID: atomic.AddUint32(&daemon.totalInvocations, 1), - cxxName: cmdLine[0], - cxxArgs: make([]string, 0, 10), - cxxIDirs: MakeIncludeDirs(), - summary: MakeInvocationSummary(), - includesCache: daemon.GetOrCreateIncludesCache(cmdLine[0]), + createTime: time.Now(), + sessionID: atomic.AddUint32(&daemon.totalInvocations, 1), + cxxName: cmdLine[0], + cxxArgs: make([]string, 0, 10), + cxxArgsIncludes: make([]string, 0, 1), + cxxIDirs: MakeIncludeDirs(), + summary: MakeInvocationSummary(), + includesCache: nil, // TODO } parseArgFile := func(key string, arg string, argIndex *int) (string, bool) { @@ -113,6 +115,14 @@ func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invoc return "" } + parsePartsArgString := func(arg string) string { + parts := strings.SplitN(arg, "=", 2) + if len(parts) != 2 { + return arg + } + return parts[1] + } + for i := 1; i < len(cmdLine); i++ { arg := cmdLine[i] if len(arg) == 0 { @@ -183,6 +193,9 @@ func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invoc invocation.cxxArgs = append(invocation.cxxArgs, "-Xclang", xArg) i++ continue + } else if strings.HasPrefix(arg, "-stdlib=") { + invocation.cxxArgsIncludes = append(invocation.cxxArgsIncludes, parsePartsArgString(arg)) + continue } } else if isSourceFileName(arg) || isHeaderFileName(arg) { if invocation.cppInFile != "" { @@ -198,6 +211,8 @@ func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invoc invocation.cxxArgs = append(invocation.cxxArgs, arg) } + invocation.includesCache = daemon.GetOrCreateIncludesCache(invocation.cxxName, invocation.cxxArgsIncludes) + if invocation.err != nil { return } From f6c07ebd8197e35934460ae9361687431cc15db5 Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Tue, 5 Aug 2025 20:32:11 +0300 Subject: [PATCH 3/3] support LLVM option style --- internal/client/invocation.go | 19 +++++--- internal/client/options-parser.go | 14 ++++++ internal/client/options-parser_test.go | 61 ++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 internal/client/options-parser.go create mode 100644 internal/client/options-parser_test.go diff --git a/internal/client/invocation.go b/internal/client/invocation.go index fd257da..b23649d 100644 --- a/internal/client/invocation.go +++ b/internal/client/invocation.go @@ -115,12 +115,19 @@ func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invoc return "" } - parsePartsArgString := func(arg string) string { + parsePartsArgString := func(arg string, argIndex *int) string { parts := strings.SplitN(arg, "=", 2) - if len(parts) != 2 { - return arg + if len(parts) == 2 { + return parts[1] + } + + if *argIndex+1 < len(cmdLine) { + *argIndex++ + return cmdLine[*argIndex] + } else { + invocation.err = fmt.Errorf("unsupported command-line: no argument after %s", arg) + return "" } - return parts[1] } for i := 1; i < len(cmdLine); i++ { @@ -193,8 +200,8 @@ func ParseCmdLineInvocation(daemon *Daemon, cwd string, cmdLine []string) (invoc invocation.cxxArgs = append(invocation.cxxArgs, "-Xclang", xArg) i++ continue - } else if strings.HasPrefix(arg, "-stdlib=") { - invocation.cxxArgsIncludes = append(invocation.cxxArgsIncludes, parsePartsArgString(arg)) + } else if HasPrefixOrEqualOption(arg, "-stdlib") || HasPrefixOrEqualOption(arg, "--stdlib") { + invocation.cxxArgsIncludes = append(invocation.cxxArgsIncludes, parsePartsArgString(arg, &i)) continue } } else if isSourceFileName(arg) || isHeaderFileName(arg) { diff --git a/internal/client/options-parser.go b/internal/client/options-parser.go new file mode 100644 index 0000000..69f0971 --- /dev/null +++ b/internal/client/options-parser.go @@ -0,0 +1,14 @@ +package client + +import ( + "fmt" + "strings" +) + +func HasPrefixOrEqualOption(optionName string, flagValue string) bool { + if flagValue == optionName || strings.HasPrefix(flagValue, fmt.Sprintf("%s=", optionName)) { + return true + } + + return false +} diff --git a/internal/client/options-parser_test.go b/internal/client/options-parser_test.go new file mode 100644 index 0000000..a5bc591 --- /dev/null +++ b/internal/client/options-parser_test.go @@ -0,0 +1,61 @@ +package client + +import "testing" + +func TestOther(t *testing.T) { + tests := []struct { + optionName string + flag string + isPrefix bool + }{ + { + optionName: "-option", + flag: "-option=args", + isPrefix: true, + }, + { + optionName: "--option", + flag: "--option=args", + isPrefix: true, + }, + { + optionName: "-option", + flag: "-option", + isPrefix: true, + }, + { + optionName: "--option", + flag: "--option", + isPrefix: true, + }, + + // Negative + { + optionName: "options", + flag: "-options", + isPrefix: false, + }, + { + optionName: "-options", + flag: "--options", + isPrefix: false, + }, + { + optionName: "--options", + flag: "-options", + isPrefix: false, + }, + { + optionName: "-option", + flag: "-options=args", + isPrefix: false, + }, + } + + for _, tt := range tests { + isPrefix := HasPrefixOrEqualOption(tt.optionName, tt.flag) + if isPrefix != tt.isPrefix { + t.Errorf("want: '%t', got: '%t' (optionName: '%s', flag: '%s')", tt.isPrefix, isPrefix, tt.optionName, tt.flag) + } + } +}