Skip to content

Commit 8a0f45a

Browse files
committed
v2: make cleanc prod work with vh cache
1 parent ffe6640 commit 8a0f45a

1 file changed

Lines changed: 140 additions & 24 deletions

File tree

‎vlib/v2/builder/builder.v‎

Lines changed: 140 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,28 @@ fn (b &Builder) inject_cached_core_forward_decls(source string) string {
10111011
if decls == '' {
10121012
return source
10131013
}
1014+
// Collect names already defined in the main source (typedefs and macros)
1015+
// to avoid duplicate/conflicting injected declarations.
1016+
mut existing_names := map[string]bool{}
1017+
for src_line in source.split_into_lines() {
1018+
trimmed := src_line.trim_space()
1019+
if trimmed.starts_with('typedef ') && trimmed.ends_with(';') {
1020+
name := extract_typedef_name(trimmed)
1021+
if name.len > 0 {
1022+
existing_names[name] = true
1023+
}
1024+
} else if trimmed.starts_with('#define ') {
1025+
// Extract macro name: "#define NAME" or "#define NAME(..."
1026+
rest := trimmed[8..]
1027+
mut end := 0
1028+
for end < rest.len && rest[end] != `(` && !rest[end].is_space() {
1029+
end++
1030+
}
1031+
if end > 0 {
1032+
existing_names[rest[..end]] = true
1033+
}
1034+
}
1035+
}
10141036
mut lines := source.split_into_lines()
10151037
mut out := []string{cap: lines.len + decls.count('\n') + 2}
10161038
mut saw_cached_decl := false
@@ -1023,9 +1045,24 @@ fn (b &Builder) inject_cached_core_forward_decls(source string) string {
10231045
}
10241046
if saw_cached_decl && line == '' && !inserted {
10251047
for decl_line in decls.split_into_lines() {
1026-
if decl_line != '' {
1027-
out << decl_line
1048+
if decl_line == '' {
1049+
continue
1050+
}
1051+
// Skip declarations whose name conflicts with main source.
1052+
if decl_line.starts_with('typedef ') {
1053+
name := extract_typedef_name(decl_line)
1054+
if name.len > 0 && name in existing_names {
1055+
continue
1056+
}
1057+
} else {
1058+
// Function forward declaration — extract fn name and skip
1059+
// if it conflicts with a #define macro in the main source.
1060+
fn_name := extract_fn_decl_name(decl_line)
1061+
if fn_name.len > 0 && fn_name in existing_names {
1062+
continue
1063+
}
10281064
}
1065+
out << decl_line
10291066
}
10301067
out << ''
10311068
inserted = true
@@ -1037,63 +1074,119 @@ fn (b &Builder) inject_cached_core_forward_decls(source string) string {
10371074
return out.join('\n')
10381075
}
10391076

1077+
fn extract_typedef_name(line string) string {
1078+
// For "typedef <something> Name;" or "typedef struct Name { ... } Name;"
1079+
// extract the name just before the final ';'.
1080+
trimmed := line.trim_right('; ')
1081+
// The typedef name is the last space-separated token, but handle
1082+
// attribute suffixes like __attribute__((...))).
1083+
if trimmed.ends_with(')') {
1084+
// Typedef with attribute — find name before __attribute__
1085+
attr_idx := trimmed.index('__attribute__') or { return '' }
1086+
before_attr := trimmed[..attr_idx].trim_space()
1087+
last_space := before_attr.last_index(' ') or { return before_attr }
1088+
return before_attr[last_space + 1..]
1089+
}
1090+
if trimmed.ends_with('}') {
1091+
// "typedef struct X { ... } X" — find name after '}'
1092+
brace_end := trimmed.last_index('}') or { return '' }
1093+
return trimmed[brace_end + 1..].trim_space()
1094+
}
1095+
last_space := trimmed.last_index(' ') or { return trimmed }
1096+
return trimmed[last_space + 1..]
1097+
}
1098+
1099+
fn extract_fn_decl_name(line string) string {
1100+
// Extract function name from a C forward declaration like:
1101+
// "string time__FormatTime__str(time__FormatTime e);"
1102+
// The name is the token before '('.
1103+
paren_idx := line.index_u8(`(`)
1104+
if paren_idx <= 0 {
1105+
return ''
1106+
}
1107+
before_paren := line[..paren_idx].trim_space()
1108+
last_space := before_paren.last_index(' ') or { return before_paren }
1109+
name := before_paren[last_space + 1..]
1110+
// Skip pointer prefixes
1111+
if name.len > 0 && name[0] == `*` {
1112+
return name[1..]
1113+
}
1114+
return name
1115+
}
1116+
10401117
fn (b &Builder) cached_core_forward_decls() string {
10411118
cache_dir := b.core_cache_dir()
10421119
mut seen := map[string]bool{}
1043-
mut decls := []string{}
1120+
mut typedefs := []string{}
1121+
mut fn_decls := []string{}
10441122
for cache_name in [builtin_cache_name, vlib_cache_name] {
10451123
c_path := os.join_path(cache_dir, '${cache_name}.c')
10461124
if !os.exists(c_path) {
10471125
continue
10481126
}
1049-
for decl in top_level_c_forward_decls(c_path) {
1050-
if decl in seen {
1127+
tds, fds := top_level_c_decls(c_path)
1128+
for td in tds {
1129+
if td in seen {
10511130
continue
10521131
}
1053-
seen[decl] = true
1054-
decls << decl
1132+
seen[td] = true
1133+
typedefs << td
1134+
}
1135+
for fd in fds {
1136+
if fd in seen {
1137+
continue
1138+
}
1139+
seen[fd] = true
1140+
fn_decls << fd
10551141
}
10561142
}
1057-
return decls.join('\n')
1143+
mut all := []string{cap: typedefs.len + fn_decls.len}
1144+
all << typedefs
1145+
all << fn_decls
1146+
return all.join('\n')
10581147
}
10591148

1060-
fn top_level_c_forward_decls(c_path string) []string {
1061-
lines := os.read_lines(c_path) or { return []string{} }
1062-
mut decls := []string{}
1149+
// top_level_c_decls extracts typedef declarations and function forward
1150+
// declarations from a cached C source file. Returns (typedefs, fn_decls).
1151+
fn top_level_c_decls(c_path string) ([]string, []string) {
1152+
lines := os.read_lines(c_path) or { return []string{}, []string{} }
1153+
mut typedefs := []string{}
1154+
mut fn_decls := []string{}
10631155
for raw_line in lines {
10641156
if raw_line.len == 0 || raw_line[0].is_space() {
10651157
continue
10661158
}
10671159
line := raw_line.trim_space()
1160+
// Collect typedef lines (struct/union/array/map forward typedefs).
1161+
if line.starts_with('typedef ') && line.ends_with(';') {
1162+
typedefs << line
1163+
continue
1164+
}
1165+
// Collect function forward declarations.
10681166
if !line.ends_with(');') || !line.contains('(') {
10691167
continue
10701168
}
1071-
if line.starts_with('#') || line.starts_with('typedef ') || line.starts_with('struct ')
1072-
|| line.starts_with('union ') || line.starts_with('enum ')
1073-
|| line.starts_with('return ') || line.starts_with('if ') || line.starts_with('for ')
1074-
|| line.starts_with('while ') || line.starts_with('switch ') {
1169+
if line.starts_with('#') || line.starts_with('struct ') || line.starts_with('union ')
1170+
|| line.starts_with('enum ') || line.starts_with('return ') || line.starts_with('if ')
1171+
|| line.starts_with('for ') || line.starts_with('while ') || line.starts_with('switch ') {
10751172
continue
10761173
}
1077-
// Skip expression fragments that start with '(' or '*'
10781174
if line[0] == `(` || line[0] == `*` {
10791175
continue
10801176
}
1081-
// Skip global variable definitions (contain '=') - only extract function declarations.
10821177
if line.contains('=') {
10831178
continue
10841179
}
1085-
// A forward declaration has at least a return type and function name before '('.
1086-
// Reject bare function calls like 'memset(...)' or 'tos(...)'.
10871180
paren_idx := line.index_u8(`(`)
10881181
if paren_idx > 0 {
10891182
before_paren := line[..paren_idx].trim_space()
10901183
if !before_paren.contains(' ') && !before_paren.contains('*') {
10911184
continue
10921185
}
10931186
}
1094-
decls << line
1187+
fn_decls << line
10951188
}
1096-
return decls
1189+
return typedefs, fn_decls
10971190
}
10981191

10991192
fn (mut b Builder) ensure_cached_module_object(cache_dir string, cache_name string, module_paths []string, emit_modules []string, cc string, cc_flags string, error_limit_flag string) !string {
@@ -1305,11 +1398,34 @@ fn flag_references_missing_file(flag string) bool {
13051398
fn (b &Builder) collect_cflags_from_sources() string {
13061399
mut flags := []string{}
13071400
mut seen := map[string]bool{}
1401+
mut scanned_files := map[string]bool{}
1402+
// Collect source file paths to scan. When .vh headers were used for
1403+
// parsing, b.files references the .vh summaries which lack #flag
1404+
// directives. Always include the original core module source files
1405+
// so that directive flags (e.g. -I paths) are never lost.
1406+
mut scan_paths := []string{}
13081407
for file in b.files {
1309-
if file.name == '' {
1408+
if file.name != '' {
1409+
scan_paths << file.name
1410+
}
1411+
}
1412+
if !b.pref.skip_builtin {
1413+
for module_path in core_cached_module_paths {
1414+
vlib_path := b.pref.get_vlib_module_path(module_path)
1415+
module_files := get_v_files_from_dir(vlib_path, b.pref.user_defines)
1416+
for mf in module_files {
1417+
if mf !in scanned_files {
1418+
scan_paths << mf
1419+
}
1420+
}
1421+
}
1422+
}
1423+
for scan_path in scan_paths {
1424+
if scan_path == '' || scan_path in scanned_files {
13101425
continue
13111426
}
1312-
lines := os.read_lines(file.name) or { continue }
1427+
scanned_files[scan_path] = true
1428+
lines := os.read_lines(scan_path) or { continue }
13131429
// Track $if nesting to skip flags inside non-matching comptime blocks.
13141430
// skip_depth > 0 means we are inside a non-matching $if block.
13151431
mut skip_depth := 0
@@ -1340,7 +1456,7 @@ fn (b &Builder) collect_cflags_from_sources() string {
13401456
if skip_depth > 0 {
13411457
continue
13421458
}
1343-
mut flag := parse_flag_directive_line(line, file.name) or { continue }
1459+
mut flag := parse_flag_directive_line(line, scan_path) or { continue }
13441460
flag = flag.replace('@VEXEROOT', b.pref.vroot).replace('VEXEROOT', b.pref.vroot)
13451461
if flag_references_missing_file(flag) {
13461462
continue

0 commit comments

Comments
 (0)