Skip to content

Commit 5495384

Browse files
authored
Merge pull request #71 from toodofun/feat/addRust&Ruby
Feat/add rust&ruby
2 parents 90a80b9 + 8513ffb commit 5495384

File tree

18 files changed

+1955
-47
lines changed

18 files changed

+1955
-47
lines changed

Makefile

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ XARGS := xargs -r
4545
COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
4646

4747
ifeq ($(origin ROOT_DIR),undefined)
48-
ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR) && pwd -P))
48+
ROOT_DIR := $(shell cd "$(COMMON_SELF_DIR)" && pwd -P)
4949
endif
5050

5151
# Create output directory
5252
ifeq ($(origin OUTPUT_DIR),undefined)
5353
OUTPUT_DIR := $(ROOT_DIR)/_output
54-
$(shell mkdir -p $(OUTPUT_DIR))
54+
$(shell mkdir -p "$(OUTPUT_DIR)")
5555
endif
5656

57-
GO_LDFLAGS := $(shell $(GO) run $(ROOT_DIR)/scripts/gen-ldflags.go)
57+
GO_LDFLAGS := $(shell $(GO) run "$(ROOT_DIR)/scripts/gen-ldflags.go")
5858
GO_BUILD_FLAGS = --ldflags "$(GO_LDFLAGS)"
5959

6060
# ==============================================================================
@@ -69,22 +69,22 @@ include scripts/Makefile.tools.mk
6969
.PHONY: lint
7070
lint: tools.verify.golangci-lint
7171
@echo "===========> Run golangci to lint source codes"
72-
@golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/...
72+
@golangci-lint run -c "$(ROOT_DIR)/.golangci.yml" "$(ROOT_DIR)/..."
7373

7474
## test: Run unit test.
7575
.PHONY: test
7676
test: tools.verify.go-junit-report
7777
@echo "===========> Run unit test"
78-
@set -o pipefail;$(GO) test -tags=test $(shell go list ./...) -race -cover -coverprofile=$(OUTPUT_DIR)/coverage.out \
78+
@set -o pipefail;$(GO) test -tags=test $(shell go list ./...) -race -cover -coverprofile="$(OUTPUT_DIR)/coverage.out" \
7979
-timeout=10m -shuffle=on -short \
80-
@$(GO) tool cover -html=$(OUTPUT_DIR)/coverage.out -o $(OUTPUT_DIR)/coverage.html
81-
@$(GO) tool cover -func=$(OUTPUT_DIR)/coverage.out
80+
@$(GO) tool cover -html="$(OUTPUT_DIR)/coverage.out" -o "$(OUTPUT_DIR)/coverage.html"
81+
@$(GO) tool cover -func="$(OUTPUT_DIR)/coverage.out"
8282

8383
## cover: Run unit test and get test coverage.
8484
.PHONY: cover
8585
cover: test
86-
@$(GO) tool cover -func=$(OUTPUT_DIR)/coverage.out | \
87-
awk -v target=$(COVERAGE) -f $(ROOT_DIR)/scripts/coverage.awk
86+
@$(GO) tool cover -func="$(OUTPUT_DIR)/coverage.out" | \
87+
awk -v target=$(COVERAGE) -f "$(ROOT_DIR)/scripts/coverage.awk"
8888

8989
## format: Gofmt (reformat) package sources (exclude vendor dir if existed).
9090
.PHONY: format
@@ -99,12 +99,12 @@ format: tools.verify.golines tools.verify.goimports
9999
.PHONY: verify-copyright
100100
verify-copyright: tools.verify.licctl
101101
@echo "===========> Verifying the boilerplate headers for all files"
102-
@licctl --check -f $(ROOT_DIR)/scripts/boilerplate.txt $(ROOT_DIR) --skip-dirs=_output,testdata,.github,.idea
102+
@licctl --check -f "$(ROOT_DIR)/scripts/boilerplate.txt" "$(ROOT_DIR)" --skip-dirs=_output,testdata,.github,.idea
103103

104104
## add-copyright: Ensures source code files have copyright license headers.
105105
.PHONY: add-copyright
106106
add-copyright: tools.verify.licctl
107-
@licctl -v -f $(ROOT_DIR)/scripts/boilerplate.txt $(ROOT_DIR) --skip-dirs=_output,testdata,.github,.idea
107+
@licctl -v -f "$(ROOT_DIR)/scripts/boilerplate.txt" "$(ROOT_DIR)" --skip-dirs=_output,testdata,.github,.idea
108108

109109
## build: generate releases for unix and windows systems
110110
.PHONY: build
@@ -127,7 +127,7 @@ image.build.%: build
127127
$(eval ARCH := $(word 2,$(subst _, ,$(PLATFORM))))
128128
@echo "===========> Building and Pushing $(TAG) for $(OS) $(ARCH) $(ROOT_DIR)/$(DOCKER_FILE)"
129129
@${DOCKER} buildx build --push -t $(TAG) --build-arg TARGETARCH=${OS}-${ARCH} --build-arg RELEASE=${DOCKER_BUILD_ARG_RELEASE} \
130-
--platform $(DOCKER_MULTI_ARCH) -f $(ROOT_DIR)/$(DOCKER_FILE) $(ROOT_DIR)
130+
--platform $(DOCKER_MULTI_ARCH) -f "$(ROOT_DIR)/$(DOCKER_FILE)" "$(ROOT_DIR)"
131131

132132
## image.push: Push docker mirror repository
133133
.PHONY: image.push

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ Use "gvm [command] --help" for more information about a command.
6363
- [x] Python
6464
- [ ] C#
6565
- [ ] C++/C
66-
- [ ] Ruby
67-
- [ ] Rust
66+
- [x] Ruby
67+
- [x] Rust
6868

6969
## 🌈  Screenshots
7070
![usage](assets/gvm.gif)

README_zh.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ Use "gvm [command] --help" for more information about a command.
6363
- [x] Python
6464
- [ ] C#
6565
- [ ] C++/C
66-
- [ ] Ruby
67-
- [ ] Rust
66+
- [x] Ruby
67+
- [x] Rust
6868

6969
## 🌈  截图
7070
![usage](assets/gvm.gif)

internal/log/gui_filter.go

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// Copyright 2025 The Toodofun Authors
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 log
16+
17+
import (
18+
"bufio"
19+
"context"
20+
"io"
21+
"strings"
22+
"sync"
23+
"time"
24+
)
25+
26+
// GUIFilterWriter 过滤编译输出,只显示重要信息给GUI用户
27+
type GUIFilterWriter struct {
28+
underlying io.Writer
29+
logger ILogger
30+
isGUI bool
31+
lastMessage string // 记录上一条消息,避免重复
32+
lastTime time.Time // 记录上次消息时间
33+
messageCount map[string]int // 记录消息类型的计数
34+
mu sync.Mutex // 保护并发访问
35+
}
36+
37+
// NewGUIFilterWriter 创建一个新的GUI过滤写入器
38+
func NewGUIFilterWriter(ctx context.Context, logger ILogger, isGUI bool) io.Writer {
39+
writer := GetWriter(ctx)
40+
return &GUIFilterWriter{
41+
underlying: writer,
42+
logger: logger,
43+
isGUI: isGUI,
44+
messageCount: make(map[string]int),
45+
}
46+
}
47+
48+
func (w *GUIFilterWriter) Write(p []byte) (int, error) {
49+
content := string(p)
50+
scanner := bufio.NewScanner(strings.NewReader(content))
51+
52+
w.mu.Lock()
53+
defer w.mu.Unlock()
54+
55+
for scanner.Scan() {
56+
line := strings.TrimSpace(scanner.Text())
57+
if w.shouldDisplayInGUI(line) {
58+
// 转换技术信息为用户友好的信息
59+
friendlyMsg := w.convertToFriendlyMessage(line)
60+
if friendlyMsg != "" {
61+
// 使用消息类型来去重,而不是完全相同的消息
62+
msgType := w.getMessageType(friendlyMsg)
63+
now := time.Now()
64+
65+
// 相同类型的消息,如果在3秒内已经显示过,则跳过
66+
if w.lastMessage != msgType || now.Sub(w.lastTime) > 3*time.Second {
67+
w.logger.Infof(friendlyMsg)
68+
w.lastMessage = msgType
69+
w.lastTime = now
70+
w.messageCount[msgType]++
71+
}
72+
}
73+
}
74+
}
75+
76+
// 如果是GUI环境,不写入原始内容到底层writer,避免显示技术细节
77+
// 如果不是GUI环境,写入完整内容用于日志文件
78+
if !w.isGUI {
79+
return w.underlying.Write(p)
80+
}
81+
82+
// GUI环境下只返回长度,表示"写入成功"但不实际输出原始内容
83+
return len(p), nil
84+
}
85+
86+
// shouldDisplayInGUI 判断是否应该在GUI中显示这行内容
87+
func (w *GUIFilterWriter) shouldDisplayInGUI(line string) bool {
88+
if line == "" {
89+
return false
90+
}
91+
92+
lineLower := strings.ToLower(line)
93+
94+
// 首先过滤掉纯技术细节(优先级最高)
95+
skipKeywords := []string{
96+
"clang version",
97+
"target:",
98+
"thread model:",
99+
"installeddir:",
100+
"/usr/bin/ruby",
101+
"rbconfig.rb",
102+
"insecure world writable",
103+
"apple clang version",
104+
"checking build system type",
105+
"checking host system type",
106+
"warning: insecure world writable",
107+
}
108+
109+
for _, keyword := range skipKeywords {
110+
if strings.Contains(lineLower, keyword) {
111+
return false
112+
}
113+
}
114+
115+
// 特殊处理:过滤掉包含insecure的警告
116+
if strings.Contains(lineLower, "warning:") && strings.Contains(lineLower, "insecure") {
117+
return false
118+
}
119+
120+
// 显示重要的进度信息
121+
importantKeywords := []string{
122+
"configure:",
123+
"checking for",
124+
"creating",
125+
"installing",
126+
"building",
127+
"linking",
128+
"generating",
129+
"compiling",
130+
"error:",
131+
"warning:",
132+
"failed",
133+
"success",
134+
"completed",
135+
"finished",
136+
}
137+
138+
for _, keyword := range importantKeywords {
139+
if strings.Contains(lineLower, keyword) {
140+
return true
141+
}
142+
}
143+
144+
return false
145+
}
146+
147+
// convertToFriendlyMessage 将技术信息转换为用户友好的信息
148+
func (w *GUIFilterWriter) convertToFriendlyMessage(line string) string {
149+
lineLower := strings.ToLower(line)
150+
151+
// 配置阶段
152+
if strings.Contains(lineLower, "configure:") {
153+
if strings.Contains(lineLower, "creating") {
154+
return "⚙️ 正在生成配置文件..."
155+
}
156+
if strings.Contains(lineLower, "error") {
157+
return "❌ 配置过程中遇到错误"
158+
}
159+
return "⚙️ 正在配置编译环境..."
160+
}
161+
162+
// 编译阶段 - 使用通用消息避免重复
163+
if strings.Contains(lineLower, "compiling") {
164+
// 检查是否是编译进度的开始
165+
if strings.Contains(line, ".c") || strings.Contains(line, ".py") || strings.Contains(line, "main") {
166+
return "🔨 正在编译源代码..."
167+
}
168+
return "" // 其他编译信息不显示,避免重复
169+
}
170+
171+
if strings.Contains(lineLower, "linking") {
172+
return "🔗 正在链接程序..."
173+
}
174+
175+
if strings.Contains(lineLower, "building") {
176+
return "🏗️ 正在构建..."
177+
}
178+
179+
if strings.Contains(lineLower, "installing") {
180+
return "📦 正在安装文件..."
181+
}
182+
183+
if strings.Contains(lineLower, "generating") {
184+
return "📝 正在生成文件..."
185+
}
186+
187+
// 检查重要的检查步骤
188+
if strings.Contains(lineLower, "checking for") {
189+
if strings.Contains(lineLower, "gcc") || strings.Contains(lineLower, "clang") {
190+
return "🔧 检查编译工具..."
191+
}
192+
if strings.Contains(lineLower, "make") {
193+
return "🔧 检查构建工具..."
194+
}
195+
// 其他检查步骤不显示,避免过多信息
196+
return ""
197+
}
198+
199+
// 错误和警告
200+
if strings.Contains(lineLower, "error") {
201+
return "❌ " + line
202+
}
203+
204+
if strings.Contains(lineLower, "warning") && !strings.Contains(lineLower, "insecure") {
205+
return "⚠️ " + line
206+
}
207+
208+
// 成功信息
209+
if strings.Contains(lineLower, "success") || strings.Contains(lineLower, "completed") {
210+
return "✅ " + line
211+
}
212+
213+
return ""
214+
}
215+
216+
// getMessageType 获取消息类型,用于去重
217+
func (w *GUIFilterWriter) getMessageType(msg string) string {
218+
if strings.Contains(msg, "🔨") {
219+
return "compiling"
220+
}
221+
if strings.Contains(msg, "🔗") {
222+
return "linking"
223+
}
224+
if strings.Contains(msg, "⚙️") {
225+
return "configuring"
226+
}
227+
if strings.Contains(msg, "📝") {
228+
return "generating"
229+
}
230+
if strings.Contains(msg, "📦") {
231+
return "installing"
232+
}
233+
if strings.Contains(msg, "🔧") {
234+
return "checking"
235+
}
236+
if strings.Contains(msg, "❌") {
237+
return "error"
238+
}
239+
if strings.Contains(msg, "⚠️") {
240+
return "warning"
241+
}
242+
if strings.Contains(msg, "✅") {
243+
return "success"
244+
}
245+
return "other"
246+
}
247+
248+
// GetFilteredStdout 获取过滤后的标准输出(用于GUI)
249+
func GetFilteredStdout(ctx context.Context) io.Writer {
250+
logger := GetLogger(ctx)
251+
return NewGUIFilterWriter(ctx, logger, true)
252+
}
253+
254+
// GetFilteredStderr 获取过滤后的标准错误输出(用于GUI)
255+
func GetFilteredStderr(ctx context.Context) io.Writer {
256+
logger := GetLogger(ctx)
257+
return NewGUIFilterWriter(ctx, logger, true)
258+
}

0 commit comments

Comments
 (0)