| ... | ... | @@ -10,9 +10,13 @@ const Allocator = std.mem.Allocator; |
| 10 | 10 | const Step = @import("../Step.zig"); |
| 11 | 11 | const Maker = @import("../../Maker.zig"); |
| 12 | 12 | |
| 13 | | const header_text = "This file was generated by ConfigHeader using the Zig Build System."; |
| 14 | | const c_generated_line = "/* " ++ header_text ++ " */\n"; |
| 15 | | const asm_generated_line = "; " ++ header_text ++ "\n"; |
| 13 | const header_text = "This file was generated by ConfigHeader using the Zig Build System."; |
| 14 | const c_generated_line = "/* " ++ header_text ++ " */\n"; |
| 15 | const asm_generated_line = "; " ++ header_text ++ "\n"; |
| 16 | |
| 17 | /// Table value is whether the value is used. |
| 18 | const ValueMap = std.array_hash_map.String(bool); |
| 19 | const Value = Configuration.Step.ConfigHeader.Value; |
| 16 | 20 | |
| 17 | 21 | pub fn make( |
| 18 | 22 | config_header: *ConfigHeader, |
| ... | ... | @@ -23,7 +27,6 @@ pub fn make( |
| 23 | 27 | _ = config_header; |
| 24 | 28 | _ = progress_node; |
| 25 | 29 | const graph = maker.graph; |
| 26 | | const gpa = maker.gpa; |
| 27 | 30 | const step = maker.stepByIndex(step_index); |
| 28 | 31 | const io = graph.io; |
| 29 | 32 | const arena = graph.arena; // TODO don't leak into the process arena |
| ... | ... | @@ -32,8 +35,20 @@ pub fn make( |
| 32 | 35 | const conf_ch = conf_step.extended.get(conf.extra).config_header; |
| 33 | 36 | const cache_root = graph.local_cache_root; |
| 34 | 37 | |
| 35 | | if (conf_ch.style.getPath()) |lp| |
| 36 | | try step.singleUnchangingWatchInput(maker, arena, lp); |
| 38 | const input_size_limit: Io.Limit = if (conf_ch.input_size_limit.value) |x| .limited64(x) else .unlimited; |
| 39 | const include_guard_override: ?[]const u8 = if (conf_ch.include_guard.value) |s| s.slice(conf) else null; |
| 40 | const include_path: []const u8 = conf_ch.include_path.slice(conf); |
| 41 | const template_file = if (conf_ch.template_file.value) |lp| |
| 42 | try maker.resolveLazyPathIndex(arena, lp, step_index) |
| 43 | else |
| 44 | null; |
| 45 | const value_pairs = conf_ch.values.slice; |
| 46 | |
| 47 | if (conf_ch.template_file.value) |lp| try step.singleUnchangingWatchInput(maker, arena, lp.get(conf)); |
| 48 | |
| 49 | var value_map: ValueMap = .empty; |
| 50 | try value_map.ensureTotalCapacity(arena, value_pairs.len); |
| 51 | for (value_pairs) |pair| value_map.putAssumeCapacityNoClobber(pair.key.slice(conf), false); |
| 37 | 52 | |
| 38 | 53 | var man = graph.cache.obtain(); |
| 39 | 54 | defer man.deinit(); |
| ... | ... | @@ -42,48 +57,49 @@ pub fn make( |
| 42 | 57 | // random bytes when ConfigHeader implementation is modified in a |
| 43 | 58 | // non-backwards-compatible way. |
| 44 | 59 | man.hash.add(@as(u32, 0xdef08d23)); |
| 45 | | man.hash.addBytes(conf_ch.include_path); |
| 46 | | man.hash.addOptionalBytes(conf_ch.include_guard_override); |
| 60 | man.hash.add(@as(u32, @bitCast(conf_ch.flags))); |
| 61 | man.hash.addBytes(include_path); |
| 62 | man.hash.addOptionalBytes(include_guard_override); |
| 47 | 63 | |
| 48 | 64 | var aw: Writer.Allocating = .init(arena); |
| 49 | 65 | defer aw.deinit(); |
| 50 | 66 | |
| 51 | 67 | switch (conf_ch.flags.style) { |
| 52 | 68 | .autoconf_undef => { |
| 53 | | const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); |
| 54 | | const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| |
| 55 | | return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err }); |
| 56 | | renderAutoConfUndef(step, contents, &aw.writer, &conf_ch.values, src_path) catch |err| switch (err) { |
| 69 | const tf = template_file.?; |
| 70 | const contents = tf.root_dir.handle.readFileAlloc(io, tf.sub_path, arena, input_size_limit) catch |err| |
| 71 | return step.fail(maker, "unable to read autoconf input file {f}: {t}", .{ tf, err }); |
| 72 | renderAutoConfUndef(maker, step, contents, &aw.writer, value_pairs, &value_map, tf) catch |err| switch (err) { |
| 57 | 73 | error.WriteFailed => return error.OutOfMemory, |
| 58 | 74 | else => |e| return e, |
| 59 | 75 | }; |
| 60 | 76 | }, |
| 61 | 77 | .autoconf_at => { |
| 62 | | const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); |
| 63 | | const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| |
| 64 | | return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err }); |
| 65 | | renderAutoconfAt(step, contents, &aw, &conf_ch.values, src_path) catch |err| switch (err) { |
| 78 | const tf = template_file.?; |
| 79 | const contents = tf.root_dir.handle.readFileAlloc(io, tf.sub_path, arena, input_size_limit) catch |err| |
| 80 | return step.fail(maker, "unable to read autoconf input file {f}: {t}", .{ tf, err }); |
| 81 | renderAutoconfAt(maker, step, contents, &aw, value_pairs, &value_map, tf) catch |err| switch (err) { |
| 66 | 82 | error.WriteFailed => return error.OutOfMemory, |
| 67 | 83 | else => |e| return e, |
| 68 | 84 | }; |
| 69 | 85 | }, |
| 70 | 86 | .cmake => { |
| 71 | | const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); |
| 72 | | const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| |
| 73 | | return step.fail("unable to read cmake input file {s}: {t}", .{ src_path, err }); |
| 74 | | renderCmake(step, contents, &aw.writer, conf_ch.values, src_path) catch |err| switch (err) { |
| 87 | const tf = template_file.?; |
| 88 | const contents = tf.root_dir.handle.readFileAlloc(io, tf.sub_path, arena, input_size_limit) catch |err| |
| 89 | return step.fail(maker, "unable to read cmake input file {f}: {t}", .{ tf, err }); |
| 90 | renderCmake(arena, maker, step, contents, &aw.writer, value_pairs, &value_map, tf) catch |err| switch (err) { |
| 75 | 91 | error.WriteFailed => return error.OutOfMemory, |
| 76 | 92 | else => |e| return e, |
| 77 | 93 | }; |
| 78 | 94 | }, |
| 79 | 95 | .blank => { |
| 80 | | renderBlank(gpa, &aw.writer, conf_ch.values, conf_ch.include_path, conf_ch.include_guard_override) catch |err| switch (err) { |
| 96 | renderBlank(conf, &aw.writer, value_pairs, &value_map, include_path, include_guard_override) catch |err| switch (err) { |
| 81 | 97 | error.WriteFailed => return error.OutOfMemory, |
| 82 | 98 | else => |e| return e, |
| 83 | 99 | }; |
| 84 | 100 | }, |
| 85 | 101 | .nasm => { |
| 86 | | renderNasm(&aw.writer, conf_ch.values) catch |err| switch (err) { |
| 102 | renderNasm(conf, &aw.writer, value_pairs, &value_map) catch |err| switch (err) { |
| 87 | 103 | error.WriteFailed => return error.OutOfMemory, |
| 88 | 104 | else => |e| return e, |
| 89 | 105 | }; |
| ... | ... | @@ -93,9 +109,9 @@ pub fn make( |
| 93 | 109 | const output = aw.written(); |
| 94 | 110 | man.hash.addBytes(output); |
| 95 | 111 | |
| 96 | | if (try step.cacheHit(&man)) { |
| 112 | if (try step.cacheHit(maker, &man)) { |
| 97 | 113 | const digest = man.final(); |
| 98 | | maker.generatedPath().* = .{ |
| 114 | maker.generatedPath(conf_ch.generated_dir).* = .{ |
| 99 | 115 | .root_dir = cache_root, |
| 100 | 116 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }), |
| 101 | 117 | }; |
| ... | ... | @@ -116,12 +132,12 @@ pub fn make( |
| 116 | 132 | const out_path_dirname = out_path.dirname().?; |
| 117 | 133 | |
| 118 | 134 | out_path_dirname.root_dir.handle.createDirPath(io, out_path_dirname.sub_path) catch |err| |
| 119 | | return step.fail("unable to make path {f}: {t}", .{ out_path_dirname, err }); |
| 135 | return step.fail(maker, "unable to make path {f}: {t}", .{ out_path_dirname, err }); |
| 120 | 136 | |
| 121 | 137 | out_path.root_dir.handle.writeFile(io, .{ .sub_path = out_path.sub_path, .data = output }) catch |err| |
| 122 | | return step.fail("unable to write file {f}: {t}", .{ out_path, err }); |
| 138 | return step.fail(maker, "unable to write file {f}: {t}", .{ out_path, err }); |
| 123 | 139 | |
| 124 | | maker.generatedPath().* = .{ |
| 140 | maker.generatedPath(conf_ch.generated_dir).* = .{ |
| 125 | 141 | .root_dir = cache_root, |
| 126 | 142 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }), |
| 127 | 143 | }; |
| ... | ... | @@ -129,21 +145,34 @@ pub fn make( |
| 129 | 145 | try step.writeManifest(maker, &man); |
| 130 | 146 | } |
| 131 | 147 | |
| 148 | fn ensureAllValuesUsed( |
| 149 | maker: *Maker, |
| 150 | step: *Step, |
| 151 | value_map: *const ValueMap, |
| 152 | src_path: Path, |
| 153 | ) Step.ExtendedMakeError!void { |
| 154 | var any_errors = false; |
| 155 | for (value_map.keys(), value_map.values()) |name, used| { |
| 156 | if (used) continue; |
| 157 | try step.addError(maker, "{f}: config header value unused: {s}", .{ src_path, name }); |
| 158 | any_errors = true; |
| 159 | } |
| 160 | if (any_errors) return error.MakeFailed; |
| 161 | } |
| 162 | |
| 132 | 163 | fn renderAutoConfUndef( |
| 164 | maker: *Maker, |
| 133 | 165 | step: *Step, |
| 134 | 166 | contents: []const u8, |
| 135 | 167 | w: *Writer, |
| 136 | | values: *const std.array_hash_map.String(Value), |
| 137 | | src_path: []const u8, |
| 168 | value_pairs: []const Value.Pair, |
| 169 | value_map: *ValueMap, |
| 170 | src_path: Path, |
| 138 | 171 | ) !void { |
| 139 | | const build = step.owner; |
| 140 | | const allocator = build.allocator; |
| 172 | const conf = &maker.scanned_config.configuration; |
| 141 | 173 | |
| 142 | 174 | try w.writeAll(c_generated_line); |
| 143 | 175 | |
| 144 | | var is_used: std.bit_set.Dynamic = try .initEmpty(allocator, values.count()); |
| 145 | | defer is_used.deinit(allocator); |
| 146 | | |
| 147 | 176 | var any_errors = false; |
| 148 | 177 | var line_index: u32 = 0; |
| 149 | 178 | var line_it = std.mem.splitScalar(u8, contents, '\n'); |
| ... | ... | @@ -161,45 +190,35 @@ fn renderAutoConfUndef( |
| 161 | 190 | continue; |
| 162 | 191 | } |
| 163 | 192 | const name = it.next().?; |
| 164 | | const index = values.getIndex(name) orelse { |
| 165 | | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 193 | const index = value_map.getIndex(name) orelse { |
| 194 | try step.addError(maker, "{f}:{d}: unspecified config header value: {s}", .{ |
| 166 | 195 | src_path, line_index + 1, name, |
| 167 | 196 | }); |
| 168 | 197 | any_errors = true; |
| 169 | 198 | continue; |
| 170 | 199 | }; |
| 171 | | is_used.set(index); |
| 172 | | try renderValueC(w, name, values.values()[index]); |
| 173 | | } |
| 174 | | |
| 175 | | var unused_value_it = is_used.iterator(.{ .kind = .unset }); |
| 176 | | while (unused_value_it.next()) |index| { |
| 177 | | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, values.keys()[index] }); |
| 178 | | any_errors = true; |
| 200 | value_map.values()[index] = true; // Set to used. |
| 201 | try renderValueC(conf, w, name, value_pairs[index].index); |
| 179 | 202 | } |
| 180 | 203 | |
| 181 | | if (any_errors) { |
| 182 | | return error.MakeFailed; |
| 183 | | } |
| 204 | try ensureAllValuesUsed(maker, step, value_map, src_path); |
| 205 | if (any_errors) return error.MakeFailed; |
| 184 | 206 | } |
| 185 | 207 | |
| 186 | 208 | fn renderAutoconfAt( |
| 209 | maker: *Maker, |
| 187 | 210 | step: *Step, |
| 188 | 211 | contents: []const u8, |
| 189 | 212 | aw: *Writer.Allocating, |
| 190 | | values: *const std.array_hash_map.String(Value), |
| 191 | | src_path: []const u8, |
| 213 | value_pairs: []const Value.Pair, |
| 214 | value_map: *const ValueMap, |
| 215 | src_path: Path, |
| 192 | 216 | ) !void { |
| 193 | | const build = step.owner; |
| 194 | | const allocator = build.allocator; |
| 195 | 217 | const w = &aw.writer; |
| 218 | const conf = &maker.scanned_config.configuration; |
| 196 | 219 | |
| 197 | 220 | try w.writeAll(c_generated_line); |
| 198 | 221 | |
| 199 | | const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); |
| 200 | | for (used) |*u| u.* = false; |
| 201 | | defer allocator.free(used); |
| 202 | | |
| 203 | 222 | var any_errors = false; |
| 204 | 223 | var line_index: u32 = 0; |
| 205 | 224 | var line_it = std.mem.splitScalar(u8, contents, '\n'); |
| ... | ... | @@ -207,19 +226,19 @@ fn renderAutoconfAt( |
| 207 | 226 | const last_line = line_it.index == line_it.buffer.len; |
| 208 | 227 | |
| 209 | 228 | const old_len = aw.written().len; |
| 210 | | expandVariablesAutoconfAt(w, line, values, used) catch |err| switch (err) { |
| 229 | expandVariablesAutoconfAt(w, line, conf, value_pairs, value_map) catch |err| switch (err) { |
| 211 | 230 | error.MissingValue => { |
| 212 | 231 | const name = aw.written()[old_len..]; |
| 213 | 232 | defer aw.shrinkRetainingCapacity(old_len); |
| 214 | | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 233 | try step.addError(maker, "{f}:{d}: error: unspecified config header value: {s}", .{ |
| 215 | 234 | src_path, line_index + 1, name, |
| 216 | 235 | }); |
| 217 | 236 | any_errors = true; |
| 218 | 237 | continue; |
| 219 | 238 | }, |
| 220 | 239 | else => { |
| 221 | | try step.addError("{s}:{d}: unable to substitute variable: error: {s}", .{ |
| 222 | | src_path, line_index + 1, @errorName(err), |
| 240 | try step.addError(maker, "{f}:{d}: unable to substitute variable: error: {t}", .{ |
| 241 | src_path, line_index + 1, err, |
| 223 | 242 | }); |
| 224 | 243 | any_errors = true; |
| 225 | 244 | continue; |
| ... | ... | @@ -228,30 +247,23 @@ fn renderAutoconfAt( |
| 228 | 247 | if (!last_line) try w.writeByte('\n'); |
| 229 | 248 | } |
| 230 | 249 | |
| 231 | | for (values.entries.slice().items(.key), used) |name, u| { |
| 232 | | if (!u) { |
| 233 | | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 234 | | any_errors = true; |
| 235 | | } |
| 236 | | } |
| 237 | | |
| 250 | try ensureAllValuesUsed(maker, step, value_map, src_path); |
| 238 | 251 | if (any_errors) return error.MakeFailed; |
| 239 | 252 | } |
| 240 | 253 | |
| 241 | 254 | fn renderCmake( |
| 255 | arena: Allocator, |
| 256 | maker: *Maker, |
| 242 | 257 | step: *Step, |
| 243 | 258 | contents: []const u8, |
| 244 | 259 | w: *Writer, |
| 245 | | values: std.array_hash_map.String(Value), |
| 246 | | src_path: []const u8, |
| 260 | value_pairs: []const Value.Pair, |
| 261 | value_map: *ValueMap, |
| 262 | src_path: Path, |
| 247 | 263 | ) !void { |
| 248 | | const build = step.owner; |
| 249 | | const allocator = build.allocator; |
| 250 | | |
| 251 | | try w.writeAll(c_generated_line); |
| 264 | const conf = &maker.scanned_config.configuration; |
| 252 | 265 | |
| 253 | | var values_copy = try values.clone(allocator); |
| 254 | | defer values_copy.deinit(allocator); |
| 266 | try w.writeAll(c_generated_line); |
| 255 | 267 | |
| 256 | 268 | var any_errors = false; |
| 257 | 269 | var line_index: u32 = 0; |
| ... | ... | @@ -259,23 +271,22 @@ fn renderCmake( |
| 259 | 271 | while (line_it.next()) |raw_line| : (line_index += 1) { |
| 260 | 272 | const last_line = line_it.index == line_it.buffer.len; |
| 261 | 273 | |
| 262 | | const line = expandVariablesCmake(allocator, raw_line, values) catch |err| switch (err) { |
| 274 | const line = expandVariablesCmake(arena, raw_line, conf, value_pairs, value_map) catch |err| switch (err) { |
| 263 | 275 | error.InvalidCharacter => { |
| 264 | | try step.addError("{s}:{d}: error: invalid character in a variable name", .{ |
| 276 | try step.addError(maker, "{f}:{d}: invalid character in a variable name", .{ |
| 265 | 277 | src_path, line_index + 1, |
| 266 | 278 | }); |
| 267 | 279 | any_errors = true; |
| 268 | 280 | continue; |
| 269 | 281 | }, |
| 270 | 282 | else => { |
| 271 | | try step.addError("{s}:{d}: unable to substitute variable: error: {s}", .{ |
| 272 | | src_path, line_index + 1, @errorName(err), |
| 283 | try step.addError(maker, "{f}:{d}: failed substituting variable: {t}", .{ |
| 284 | src_path, line_index + 1, err, |
| 273 | 285 | }); |
| 274 | 286 | any_errors = true; |
| 275 | 287 | continue; |
| 276 | 288 | }, |
| 277 | 289 | }; |
| 278 | | defer allocator.free(line); |
| 279 | 290 | |
| 280 | 291 | const line_start = std.mem.findNone(u8, line, " \t\r") orelse { |
| 281 | 292 | try w.writeAll(line); |
| ... | ... | @@ -293,152 +304,134 @@ fn renderCmake( |
| 293 | 304 | |
| 294 | 305 | var it = std.mem.tokenizeAny(u8, trimmed_line[1..], " \t\r"); |
| 295 | 306 | const cmakedefine = it.next().?; |
| 296 | | if (!std.mem.eql(u8, cmakedefine, "cmakedefine") and |
| 297 | | !std.mem.eql(u8, cmakedefine, "cmakedefine01")) |
| 298 | | { |
| 307 | |
| 308 | const booldefine = if (std.mem.eql(u8, cmakedefine, "cmakedefine01")) |
| 309 | true |
| 310 | else if (std.mem.eql(u8, cmakedefine, "cmakedefine")) |
| 311 | false |
| 312 | else { |
| 299 | 313 | try w.writeAll(line); |
| 300 | 314 | if (!last_line) try w.writeByte('\n'); |
| 301 | 315 | continue; |
| 302 | | } |
| 303 | | |
| 304 | | const booldefine = std.mem.eql(u8, cmakedefine, "cmakedefine01"); |
| 316 | }; |
| 305 | 317 | |
| 306 | 318 | const name = it.next() orelse { |
| 307 | | try step.addError("{s}:{d}: error: missing define name", .{ |
| 308 | | src_path, line_index + 1, |
| 309 | | }); |
| 319 | try step.addError(maker, "{f}:{d}: error: missing define name", .{ src_path, line_index + 1 }); |
| 310 | 320 | any_errors = true; |
| 311 | 321 | continue; |
| 312 | 322 | }; |
| 313 | | var value = values_copy.get(name) orelse blk: { |
| 314 | | if (booldefine) { |
| 315 | | break :blk Value{ .int = 0 }; |
| 316 | | } |
| 317 | | break :blk Value.undef; |
| 323 | const orig_value: Value.Index = v: { |
| 324 | const index = value_map.getIndex(name) orelse break :v if (booldefine) .int_0 else .undef; |
| 325 | value_map.values()[index] = true; // Mark as used. |
| 326 | break :v value_pairs[index].index; |
| 318 | 327 | }; |
| 319 | | |
| 320 | | value = blk: { |
| 321 | | switch (value) { |
| 322 | | .boolean => |b| { |
| 323 | | if (!b) { |
| 324 | | break :blk Value.undef; |
| 325 | | } |
| 326 | | }, |
| 327 | | .int => |i| { |
| 328 | | if (i == 0) { |
| 329 | | break :blk Value.undef; |
| 330 | | } |
| 331 | | }, |
| 332 | | .string => |string| { |
| 333 | | if (string.len == 0) { |
| 334 | | break :blk Value.undef; |
| 335 | | } |
| 336 | | }, |
| 337 | | |
| 338 | | else => {}, |
| 339 | | } |
| 340 | | break :blk value; |
| 328 | const value = switch (orig_value.unpack(conf)) { |
| 329 | .bool => |b| if (!b) .undef else orig_value, |
| 330 | inline .i64, .u64 => |i| if (i == 0) .undef else orig_value, |
| 331 | .string => |s| if (s.len == 0) .undef else orig_value, |
| 332 | else => orig_value, |
| 341 | 333 | }; |
| 342 | 334 | |
| 335 | try w.writeAll(whitespace_prefix); |
| 336 | |
| 343 | 337 | if (booldefine) { |
| 344 | | value = blk: { |
| 345 | | switch (value) { |
| 346 | | .undef => { |
| 347 | | break :blk Value{ .boolean = false }; |
| 348 | | }, |
| 349 | | .defined => { |
| 350 | | break :blk Value{ .boolean = false }; |
| 351 | | }, |
| 352 | | .boolean => |b| { |
| 353 | | break :blk Value{ .boolean = b }; |
| 354 | | }, |
| 355 | | .int => |i| { |
| 356 | | break :blk Value{ .boolean = i != 0 }; |
| 357 | | }, |
| 358 | | .string => |string| { |
| 359 | | break :blk Value{ .boolean = string.len != 0 }; |
| 360 | | }, |
| 361 | | |
| 362 | | else => { |
| 363 | | break :blk Value{ .boolean = false }; |
| 364 | | }, |
| 365 | | } |
| 366 | | }; |
| 367 | | } else if (value != Value.undef) { |
| 368 | | value = Value{ .ident = it.rest() }; |
| 338 | try renderValueCBool(w, name, switch (value.unpack(conf)) { |
| 339 | .undef, .defined => false, |
| 340 | .bool => |b| b, |
| 341 | inline .u64, .i64 => |i| i != 0, |
| 342 | .string => |s| s.len != 0, |
| 343 | .ident => false, |
| 344 | }); |
| 345 | } else if (value != .undef) { |
| 346 | try renderValueCIdent(w, name, it.rest()); |
| 347 | } else { |
| 348 | try renderValueC(conf, w, name, value); |
| 369 | 349 | } |
| 370 | | |
| 371 | | try w.writeAll(whitespace_prefix); |
| 372 | | try renderValueC(w, name, value); |
| 373 | 350 | } |
| 374 | 351 | |
| 375 | | if (any_errors) { |
| 376 | | return error.HeaderConfigFailed; |
| 377 | | } |
| 352 | try ensureAllValuesUsed(maker, step, value_map, src_path); |
| 353 | if (any_errors) return error.MakeFailed; |
| 378 | 354 | } |
| 379 | 355 | |
| 380 | 356 | fn renderBlank( |
| 381 | | gpa: std.mem.Allocator, |
| 357 | conf: *const Configuration, |
| 382 | 358 | w: *Writer, |
| 383 | | defines: std.array_hash_map.String(Value), |
| 359 | value_pairs: []const Value.Pair, |
| 360 | value_map: *const ValueMap, |
| 384 | 361 | include_path: []const u8, |
| 385 | 362 | include_guard_override: ?[]const u8, |
| 386 | 363 | ) !void { |
| 387 | 364 | try w.writeAll(c_generated_line); |
| 388 | 365 | |
| 389 | | const include_guard_name = include_guard_override orelse blk: { |
| 390 | | const name = try gpa.dupe(u8, include_path); |
| 391 | | for (name) |*byte| { |
| 392 | | switch (byte.*) { |
| 393 | | 'a'...'z' => byte.* = byte.* - 'a' + 'A', |
| 394 | | 'A'...'Z', '0'...'9' => continue, |
| 395 | | else => byte.* = '_', |
| 396 | | } |
| 397 | | } |
| 398 | | break :blk name; |
| 366 | const include_guard_fmt: IncludeGuardFmt = .{ |
| 367 | .include_path = include_path, |
| 368 | .override = include_guard_override, |
| 399 | 369 | }; |
| 400 | | defer if (include_guard_override == null) gpa.free(include_guard_name); |
| 401 | 370 | |
| 402 | 371 | try w.print( |
| 403 | | \\#ifndef {[0]s} |
| 404 | | \\#define {[0]s} |
| 372 | \\#ifndef {[0]f} |
| 373 | \\#define {[0]f} |
| 405 | 374 | \\ |
| 406 | | , .{include_guard_name}); |
| 375 | , .{include_guard_fmt}); |
| 407 | 376 | |
| 408 | | const values = defines.values(); |
| 409 | | for (defines.keys(), 0..) |name, i| try renderValueC(w, name, values[i]); |
| 377 | for (value_map.keys(), value_pairs) |name, pair| try renderValueC(conf, w, name, pair.index); |
| 410 | 378 | |
| 411 | 379 | try w.print( |
| 412 | | \\#endif /* {s} */ |
| 380 | \\#endif /* {f} */ |
| 413 | 381 | \\ |
| 414 | | , .{include_guard_name}); |
| 382 | , .{include_guard_fmt}); |
| 415 | 383 | } |
| 416 | 384 | |
| 417 | | fn renderNasm(w: *Writer, defines: std.array_hash_map.String(Value)) !void { |
| 385 | const IncludeGuardFmt = struct { |
| 386 | include_path: []const u8, |
| 387 | override: ?[]const u8, |
| 388 | |
| 389 | pub fn format(this: @This(), w: *Writer) Writer.Error!void { |
| 390 | if (this.override) |s| return w.writeAll(s); |
| 391 | for (this.include_path) |byte| switch (byte) { |
| 392 | 'a'...'z' => try w.writeByte(byte - 'a' + 'A'), |
| 393 | 'A'...'Z', '0'...'9' => continue, |
| 394 | else => try w.writeByte('_'), |
| 395 | }; |
| 396 | } |
| 397 | }; |
| 398 | |
| 399 | fn renderNasm( |
| 400 | conf: *const Configuration, |
| 401 | w: *Writer, |
| 402 | value_pairs: []const Value.Pair, |
| 403 | value_map: *const ValueMap, |
| 404 | ) !void { |
| 418 | 405 | try w.writeAll(asm_generated_line); |
| 419 | | for (defines.keys(), defines.values()) |name, value| try renderValueNasm(w, name, value); |
| 406 | for (value_map.keys(), value_pairs) |name, pair| try renderValueNasm(conf, w, name, pair.index); |
| 420 | 407 | } |
| 421 | 408 | |
| 422 | | fn renderValueC(w: *Writer, name: []const u8, value: Value) !void { |
| 423 | | switch (value) { |
| 409 | fn renderValueC(conf: *const Configuration, w: *Writer, name: []const u8, value: Value.Index) !void { |
| 410 | switch (value.unpack(conf)) { |
| 424 | 411 | .undef => try w.print("/* #undef {s} */\n", .{name}), |
| 425 | 412 | .defined => try w.print("#define {s}\n", .{name}), |
| 426 | | .boolean => |b| try w.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), |
| 427 | | .int => |i| try w.print("#define {s} {d}\n", .{ name, i }), |
| 428 | | .ident => |ident| try w.print("#define {s} {s}\n", .{ name, ident }), |
| 429 | | // TODO: use C-specific escaping instead of zig string literals |
| 413 | .bool => |b| return renderValueCBool(w, name, b), |
| 414 | inline .u64, .i64 => |i| try w.print("#define {s} {d}\n", .{ name, i }), |
| 415 | .ident => |ident| return renderValueCIdent(w, name, ident), |
| 430 | 416 | .string => |string| try w.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), |
| 431 | 417 | } |
| 432 | 418 | } |
| 433 | 419 | |
| 434 | | fn renderValueNasm(w: *Writer, name: []const u8, value: Value) !void { |
| 435 | | switch (value) { |
| 420 | fn renderValueCIdent(w: *Writer, name: []const u8, ident: []const u8) Writer.Error!void { |
| 421 | return w.print("#define {s} {s}\n", .{ name, ident }); |
| 422 | } |
| 423 | |
| 424 | fn renderValueCBool(w: *Writer, name: []const u8, b: bool) Writer.Error!void { |
| 425 | return w.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }); |
| 426 | } |
| 427 | |
| 428 | fn renderValueNasm(conf: *const Configuration, w: *Writer, name: []const u8, value: Value.Index) !void { |
| 429 | switch (value.unpack(conf)) { |
| 436 | 430 | .undef => try w.print("; %undef {s}\n", .{name}), |
| 437 | 431 | .defined => try w.print("%define {s}\n", .{name}), |
| 438 | | .boolean => |b| try w.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), |
| 439 | | .int => |i| try w.print("%define {s} {d}\n", .{ name, i }), |
| 432 | .bool => |b| try w.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), |
| 433 | inline .u64, .i64 => |i| try w.print("%define {s} {d}\n", .{ name, i }), |
| 440 | 434 | .ident => |ident| try w.print("%define {s} {s}\n", .{ name, ident }), |
| 441 | | // TODO: use nasm-specific escaping instead of zig string literals |
| 442 | 435 | .string => |string| try w.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), |
| 443 | 436 | } |
| 444 | 437 | } |
| ... | ... | @@ -446,8 +439,9 @@ fn renderValueNasm(w: *Writer, name: []const u8, value: Value) !void { |
| 446 | 439 | fn expandVariablesAutoconfAt( |
| 447 | 440 | w: *Writer, |
| 448 | 441 | contents: []const u8, |
| 449 | | values: *const std.array_hash_map.String(Value), |
| 450 | | used: []bool, |
| 442 | conf: *const Configuration, |
| 443 | value_pairs: []const Value.Pair, |
| 444 | value_map: *const ValueMap, |
| 451 | 445 | ) !void { |
| 452 | 446 | const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; |
| 453 | 447 | |
| ... | ... | @@ -467,18 +461,18 @@ fn expandVariablesAutoconfAt( |
| 467 | 461 | } |
| 468 | 462 | |
| 469 | 463 | const key = contents[curr + 1 .. close_pos]; |
| 470 | | const index = values.getIndex(key) orelse { |
| 464 | const index = value_map.getIndex(key) orelse { |
| 471 | 465 | // Report the missing key to the caller. |
| 472 | 466 | try w.writeAll(key); |
| 473 | 467 | return error.MissingValue; |
| 474 | 468 | }; |
| 475 | | const value = values.entries.slice().items(.value)[index]; |
| 476 | | used[index] = true; |
| 469 | const value = value_pairs[index].index; |
| 470 | value_map.values()[index] = true; // Mark as used. |
| 477 | 471 | try w.writeAll(contents[source_offset..curr]); |
| 478 | | switch (value) { |
| 472 | switch (value.unpack(conf)) { |
| 479 | 473 | .undef, .defined => {}, |
| 480 | | .boolean => |b| try w.writeByte(@as(u8, '0') + @intFromBool(b)), |
| 481 | | .int => |i| try w.print("{d}", .{i}), |
| 474 | .bool => |b| try w.writeByte(@as(u8, '0') + @intFromBool(b)), |
| 475 | inline .u64, .i64 => |i| try w.print("{d}", .{i}), |
| 482 | 476 | .ident, .string => |s| try w.writeAll(s), |
| 483 | 477 | } |
| 484 | 478 | |
| ... | ... | @@ -491,12 +485,13 @@ fn expandVariablesAutoconfAt( |
| 491 | 485 | } |
| 492 | 486 | |
| 493 | 487 | fn expandVariablesCmake( |
| 494 | | allocator: Allocator, |
| 488 | arena: Allocator, |
| 495 | 489 | contents: []const u8, |
| 496 | | values: std.array_hash_map.String(Value), |
| 490 | conf: *const Configuration, |
| 491 | value_pairs: []const Value.Pair, |
| 492 | value_map: *const ValueMap, |
| 497 | 493 | ) ![]const u8 { |
| 498 | | var result: std.array_list.Managed(u8) = .init(allocator); |
| 499 | | errdefer result.deinit(); |
| 494 | var result: std.ArrayList(u8) = .empty; |
| 500 | 495 | |
| 501 | 496 | const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/_.+-"; |
| 502 | 497 | const open_var = "${"; |
| ... | ... | @@ -507,8 +502,7 @@ fn expandVariablesCmake( |
| 507 | 502 | source: usize, |
| 508 | 503 | target: usize, |
| 509 | 504 | }; |
| 510 | | var var_stack: std.array_list.Managed(Position) = .init(allocator); |
| 511 | | defer var_stack.deinit(); |
| 505 | var var_stack: std.ArrayList(Position) = .empty; |
| 512 | 506 | loop: while (curr < contents.len) : (curr += 1) { |
| 513 | 507 | switch (contents[curr]) { |
| 514 | 508 | '@' => blk: { |
| ... | ... | @@ -524,20 +518,16 @@ fn expandVariablesCmake( |
| 524 | 518 | } |
| 525 | 519 | |
| 526 | 520 | const key = contents[curr + 1 .. close_pos]; |
| 527 | | const value = values.get(key) orelse return error.MissingValue; |
| 521 | const index = value_map.getIndex(key) orelse return error.MissingValue; |
| 522 | value_map.values()[index] = true; // Mark as used. |
| 523 | const value = value_pairs[index].index; |
| 528 | 524 | const missing = contents[source_offset..curr]; |
| 529 | | try result.appendSlice(missing); |
| 530 | | switch (value) { |
| 525 | try result.appendSlice(arena, missing); |
| 526 | switch (value.unpack(conf)) { |
| 531 | 527 | .undef, .defined => {}, |
| 532 | | .boolean => |b| { |
| 533 | | try result.append(if (b) '1' else '0'); |
| 534 | | }, |
| 535 | | .int => |i| { |
| 536 | | try result.print("{d}", .{i}); |
| 537 | | }, |
| 538 | | .ident, .string => |s| { |
| 539 | | try result.appendSlice(s); |
| 540 | | }, |
| 528 | .bool => |b| try result.append(arena, if (b) '1' else '0'), |
| 529 | inline .i64, .u64 => |i| try result.print(arena, "{d}", .{i}), |
| 530 | .ident, .string => |s| try result.appendSlice(arena, s), |
| 541 | 531 | } |
| 542 | 532 | |
| 543 | 533 | curr = close_pos; |
| ... | ... | @@ -553,12 +543,12 @@ fn expandVariablesCmake( |
| 553 | 543 | break :blk; |
| 554 | 544 | } |
| 555 | 545 | const missing = contents[source_offset..curr]; |
| 556 | | try result.appendSlice(missing); |
| 557 | | try result.appendSlice(open_var); |
| 546 | try result.appendSlice(arena, missing); |
| 547 | try result.appendSlice(arena, open_var); |
| 558 | 548 | |
| 559 | 549 | source_offset = curr + open_var.len; |
| 560 | 550 | curr = next; |
| 561 | | try var_stack.append(Position{ |
| 551 | try var_stack.append(arena, .{ |
| 562 | 552 | .source = curr, |
| 563 | 553 | .target = result.items.len - open_var.len, |
| 564 | 554 | }); |
| ... | ... | @@ -575,26 +565,22 @@ fn expandVariablesCmake( |
| 575 | 565 | source_offset += open_var.len; |
| 576 | 566 | } |
| 577 | 567 | const missing = contents[source_offset..curr]; |
| 578 | | try result.appendSlice(missing); |
| 568 | try result.appendSlice(arena, missing); |
| 579 | 569 | |
| 580 | 570 | const key_start = open_pos.target + open_var.len; |
| 581 | 571 | const key = result.items[key_start..]; |
| 582 | 572 | if (key.len == 0) { |
| 583 | 573 | return error.MissingKey; |
| 584 | 574 | } |
| 585 | | const value = values.get(key) orelse return error.MissingValue; |
| 575 | const index = value_map.getIndex(key) orelse return error.MissingValue; |
| 576 | value_map.values()[index] = true; // Mark as used. |
| 577 | const value = value_pairs[index].index; |
| 586 | 578 | result.shrinkRetainingCapacity(result.items.len - key.len - open_var.len); |
| 587 | | switch (value) { |
| 579 | switch (value.unpack(conf)) { |
| 588 | 580 | .undef, .defined => {}, |
| 589 | | .boolean => |b| { |
| 590 | | try result.append(if (b) '1' else '0'); |
| 591 | | }, |
| 592 | | .int => |i| { |
| 593 | | try result.print("{d}", .{i}); |
| 594 | | }, |
| 595 | | .ident, .string => |s| { |
| 596 | | try result.appendSlice(s); |
| 597 | | }, |
| 581 | .bool => |b| try result.append(arena, if (b) '1' else '0'), |
| 582 | inline .i64, .u64 => |i| try result.print(arena, "{d}", .{i}), |
| 583 | .ident, .string => |s| try result.appendSlice(arena, s), |
| 598 | 584 | } |
| 599 | 585 | |
| 600 | 586 | source_offset = curr + 1; |
| ... | ... | @@ -615,320 +601,320 @@ fn expandVariablesCmake( |
| 615 | 601 | |
| 616 | 602 | if (source_offset != contents.len) { |
| 617 | 603 | const missing = contents[source_offset..]; |
| 618 | | try result.appendSlice(missing); |
| 604 | try result.appendSlice(arena, missing); |
| 619 | 605 | } |
| 620 | 606 | |
| 621 | | return result.toOwnedSlice(); |
| 607 | try result.shrinkToLen(arena); |
| 608 | |
| 609 | return result.toOwnedSliceAssert(); |
| 622 | 610 | } |
| 623 | 611 | |
| 624 | 612 | fn testReplaceVariablesAutoconfAt( |
| 625 | | allocator: Allocator, |
| 613 | arena: Allocator, |
| 626 | 614 | contents: []const u8, |
| 627 | 615 | expected: []const u8, |
| 628 | | values: std.array_hash_map.String(Value), |
| 616 | value_map: *const ValueMap, |
| 629 | 617 | ) !void { |
| 630 | | var aw: Writer.Allocating = .init(allocator); |
| 618 | var aw: Writer.Allocating = .init(arena); |
| 631 | 619 | defer aw.deinit(); |
| 632 | 620 | |
| 633 | | const used = try allocator.alloc(bool, values.count()); |
| 621 | const used = try arena.alloc(bool, value_map.count()); |
| 634 | 622 | for (used) |*u| u.* = false; |
| 635 | | defer allocator.free(used); |
| 636 | 623 | |
| 637 | | try expandVariablesAutoconfAt(&aw.writer, contents, values, used); |
| 624 | try expandVariablesAutoconfAt(&aw.writer, contents, value_map, used); |
| 638 | 625 | |
| 639 | 626 | for (used) |u| if (!u) return error.UnusedValue; |
| 640 | 627 | try std.testing.expectEqualStrings(expected, aw.written()); |
| 641 | 628 | } |
| 642 | 629 | |
| 643 | 630 | fn testReplaceVariablesCMake( |
| 644 | | allocator: Allocator, |
| 631 | arena: Allocator, |
| 645 | 632 | contents: []const u8, |
| 646 | 633 | expected: []const u8, |
| 647 | | values: std.array_hash_map.String(Value), |
| 634 | value_map: *const ValueMap, |
| 648 | 635 | ) !void { |
| 649 | | const actual = try expandVariablesCmake(allocator, contents, values); |
| 650 | | defer allocator.free(actual); |
| 636 | const actual = try expandVariablesCmake(arena, contents, value_map); |
| 651 | 637 | |
| 652 | 638 | try std.testing.expectEqualStrings(expected, actual); |
| 653 | 639 | } |
| 654 | 640 | |
| 655 | 641 | test "expandVariablesAutoconfAt simple cases" { |
| 656 | 642 | const allocator = std.testing.allocator; |
| 657 | | var values: std.array_hash_map.String(Value) = .init(allocator); |
| 658 | | defer values.deinit(); |
| 643 | var value_map: ValueMap = .empty; |
| 644 | defer value_map.deinit(); |
| 659 | 645 | |
| 660 | 646 | // empty strings are preserved |
| 661 | | try testReplaceVariablesAutoconfAt(allocator, "", "", values); |
| 647 | try testReplaceVariablesAutoconfAt(allocator, "", "", value_map); |
| 662 | 648 | |
| 663 | 649 | // line with misc content is preserved |
| 664 | | try testReplaceVariablesAutoconfAt(allocator, "no substitution", "no substitution", values); |
| 650 | try testReplaceVariablesAutoconfAt(allocator, "no substitution", "no substitution", value_map); |
| 665 | 651 | |
| 666 | 652 | // empty @ sigils are preserved |
| 667 | | try testReplaceVariablesAutoconfAt(allocator, "@", "@", values); |
| 668 | | try testReplaceVariablesAutoconfAt(allocator, "@@", "@@", values); |
| 669 | | try testReplaceVariablesAutoconfAt(allocator, "@@@", "@@@", values); |
| 670 | | try testReplaceVariablesAutoconfAt(allocator, "@@@@", "@@@@", values); |
| 653 | try testReplaceVariablesAutoconfAt(allocator, "@", "@", value_map); |
| 654 | try testReplaceVariablesAutoconfAt(allocator, "@@", "@@", value_map); |
| 655 | try testReplaceVariablesAutoconfAt(allocator, "@@@", "@@@", value_map); |
| 656 | try testReplaceVariablesAutoconfAt(allocator, "@@@@", "@@@@", value_map); |
| 671 | 657 | |
| 672 | 658 | // simple substitution |
| 673 | | try values.putNoClobber("undef", .undef); |
| 674 | | try testReplaceVariablesAutoconfAt(allocator, "@undef@", "", values); |
| 675 | | values.clearRetainingCapacity(); |
| 659 | try value_map.putNoClobber("undef", .undef); |
| 660 | try testReplaceVariablesAutoconfAt(allocator, "@undef@", "", value_map); |
| 661 | value_map.clearRetainingCapacity(); |
| 676 | 662 | |
| 677 | | try values.putNoClobber("defined", .defined); |
| 678 | | try testReplaceVariablesAutoconfAt(allocator, "@defined@", "", values); |
| 679 | | values.clearRetainingCapacity(); |
| 663 | try value_map.putNoClobber("defined", .defined); |
| 664 | try testReplaceVariablesAutoconfAt(allocator, "@defined@", "", value_map); |
| 665 | value_map.clearRetainingCapacity(); |
| 680 | 666 | |
| 681 | | try values.putNoClobber("true", Value{ .boolean = true }); |
| 682 | | try testReplaceVariablesAutoconfAt(allocator, "@true@", "1", values); |
| 683 | | values.clearRetainingCapacity(); |
| 667 | try value_map.putNoClobber("true", Value{ .boolean = true }); |
| 668 | try testReplaceVariablesAutoconfAt(allocator, "@true@", "1", value_map); |
| 669 | value_map.clearRetainingCapacity(); |
| 684 | 670 | |
| 685 | | try values.putNoClobber("false", Value{ .boolean = false }); |
| 686 | | try testReplaceVariablesAutoconfAt(allocator, "@false@", "0", values); |
| 687 | | values.clearRetainingCapacity(); |
| 671 | try value_map.putNoClobber("false", Value{ .boolean = false }); |
| 672 | try testReplaceVariablesAutoconfAt(allocator, "@false@", "0", value_map); |
| 673 | value_map.clearRetainingCapacity(); |
| 688 | 674 | |
| 689 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 690 | | try testReplaceVariablesAutoconfAt(allocator, "@int@", "42", values); |
| 691 | | values.clearRetainingCapacity(); |
| 675 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 676 | try testReplaceVariablesAutoconfAt(allocator, "@int@", "42", value_map); |
| 677 | value_map.clearRetainingCapacity(); |
| 692 | 678 | |
| 693 | | try values.putNoClobber("ident", Value{ .string = "value" }); |
| 694 | | try testReplaceVariablesAutoconfAt(allocator, "@ident@", "value", values); |
| 695 | | values.clearRetainingCapacity(); |
| 679 | try value_map.putNoClobber("ident", Value{ .string = "value" }); |
| 680 | try testReplaceVariablesAutoconfAt(allocator, "@ident@", "value", value_map); |
| 681 | value_map.clearRetainingCapacity(); |
| 696 | 682 | |
| 697 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 698 | | try testReplaceVariablesAutoconfAt(allocator, "@string@", "text", values); |
| 699 | | values.clearRetainingCapacity(); |
| 683 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 684 | try testReplaceVariablesAutoconfAt(allocator, "@string@", "text", value_map); |
| 685 | value_map.clearRetainingCapacity(); |
| 700 | 686 | |
| 701 | 687 | // double packed substitution |
| 702 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 703 | | try testReplaceVariablesAutoconfAt(allocator, "@string@@string@", "texttext", values); |
| 704 | | values.clearRetainingCapacity(); |
| 688 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 689 | try testReplaceVariablesAutoconfAt(allocator, "@string@@string@", "texttext", value_map); |
| 690 | value_map.clearRetainingCapacity(); |
| 705 | 691 | |
| 706 | 692 | // triple packed substitution |
| 707 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 708 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 709 | | try testReplaceVariablesAutoconfAt(allocator, "@string@@int@@string@", "text42text", values); |
| 710 | | values.clearRetainingCapacity(); |
| 693 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 694 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 695 | try testReplaceVariablesAutoconfAt(allocator, "@string@@int@@string@", "text42text", value_map); |
| 696 | value_map.clearRetainingCapacity(); |
| 711 | 697 | |
| 712 | 698 | // double separated substitution |
| 713 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 714 | | try testReplaceVariablesAutoconfAt(allocator, "@int@.@int@", "42.42", values); |
| 715 | | values.clearRetainingCapacity(); |
| 699 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 700 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@int@", "42.42", value_map); |
| 701 | value_map.clearRetainingCapacity(); |
| 716 | 702 | |
| 717 | 703 | // triple separated substitution |
| 718 | | try values.putNoClobber("true", Value{ .boolean = true }); |
| 719 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 720 | | try testReplaceVariablesAutoconfAt(allocator, "@int@.@true@.@int@", "42.1.42", values); |
| 721 | | values.clearRetainingCapacity(); |
| 704 | try value_map.putNoClobber("true", Value{ .boolean = true }); |
| 705 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 706 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@true@.@int@", "42.1.42", value_map); |
| 707 | value_map.clearRetainingCapacity(); |
| 722 | 708 | |
| 723 | 709 | // misc prefix is preserved |
| 724 | | try values.putNoClobber("false", Value{ .boolean = false }); |
| 725 | | try testReplaceVariablesAutoconfAt(allocator, "false is @false@", "false is 0", values); |
| 726 | | values.clearRetainingCapacity(); |
| 710 | try value_map.putNoClobber("false", Value{ .boolean = false }); |
| 711 | try testReplaceVariablesAutoconfAt(allocator, "false is @false@", "false is 0", value_map); |
| 712 | value_map.clearRetainingCapacity(); |
| 727 | 713 | |
| 728 | 714 | // misc suffix is preserved |
| 729 | | try values.putNoClobber("true", Value{ .boolean = true }); |
| 730 | | try testReplaceVariablesAutoconfAt(allocator, "@true@ is true", "1 is true", values); |
| 731 | | values.clearRetainingCapacity(); |
| 715 | try value_map.putNoClobber("true", Value{ .boolean = true }); |
| 716 | try testReplaceVariablesAutoconfAt(allocator, "@true@ is true", "1 is true", value_map); |
| 717 | value_map.clearRetainingCapacity(); |
| 732 | 718 | |
| 733 | 719 | // surrounding content is preserved |
| 734 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 735 | | try testReplaceVariablesAutoconfAt(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", values); |
| 736 | | values.clearRetainingCapacity(); |
| 720 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 721 | try testReplaceVariablesAutoconfAt(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", value_map); |
| 722 | value_map.clearRetainingCapacity(); |
| 737 | 723 | |
| 738 | 724 | // incomplete key is preserved |
| 739 | | try testReplaceVariablesAutoconfAt(allocator, "@undef", "@undef", values); |
| 725 | try testReplaceVariablesAutoconfAt(allocator, "@undef", "@undef", value_map); |
| 740 | 726 | |
| 741 | 727 | // unknown key leads to an error |
| 742 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesAutoconfAt(allocator, "@bad@", "", values)); |
| 728 | try std.testing.expectError(error.MissingValue, testReplaceVariablesAutoconfAt(allocator, "@bad@", "", value_map)); |
| 743 | 729 | |
| 744 | 730 | // unused key leads to an error |
| 745 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 746 | | try values.putNoClobber("false", Value{ .boolean = false }); |
| 747 | | try std.testing.expectError(error.UnusedValue, testReplaceVariablesAutoconfAt(allocator, "@int", "", values)); |
| 748 | | values.clearRetainingCapacity(); |
| 731 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 732 | try value_map.putNoClobber("false", Value{ .boolean = false }); |
| 733 | try std.testing.expectError(error.UnusedValue, testReplaceVariablesAutoconfAt(allocator, "@int", "", value_map)); |
| 734 | value_map.clearRetainingCapacity(); |
| 749 | 735 | } |
| 750 | 736 | |
| 751 | 737 | test "expandVariablesAutoconfAt edge cases" { |
| 752 | 738 | const allocator = std.testing.allocator; |
| 753 | | var values: std.array_hash_map.String(Value) = .init(allocator); |
| 754 | | defer values.deinit(); |
| 739 | var value_map: std.array_hash_map.String(Value) = .init(allocator); |
| 740 | defer value_map.deinit(); |
| 755 | 741 | |
| 756 | 742 | // @-vars resolved only when they wrap valid characters, otherwise considered literals |
| 757 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 758 | | try testReplaceVariablesAutoconfAt(allocator, "@@string@@", "@text@", values); |
| 759 | | values.clearRetainingCapacity(); |
| 743 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 744 | try testReplaceVariablesAutoconfAt(allocator, "@@string@@", "@text@", value_map); |
| 745 | value_map.clearRetainingCapacity(); |
| 760 | 746 | |
| 761 | 747 | // expanded variables are considered strings after expansion |
| 762 | | try values.putNoClobber("string_at", Value{ .string = "@string@" }); |
| 763 | | try testReplaceVariablesAutoconfAt(allocator, "@string_at@", "@string@", values); |
| 764 | | values.clearRetainingCapacity(); |
| 748 | try value_map.putNoClobber("string_at", Value{ .string = "@string@" }); |
| 749 | try testReplaceVariablesAutoconfAt(allocator, "@string_at@", "@string@", value_map); |
| 750 | value_map.clearRetainingCapacity(); |
| 765 | 751 | } |
| 766 | 752 | |
| 767 | 753 | test "expandVariablesCmake simple cases" { |
| 768 | 754 | const allocator = std.testing.allocator; |
| 769 | | var values: std.array_hash_map.String(Value) = .init(allocator); |
| 770 | | defer values.deinit(); |
| 755 | var value_map: std.array_hash_map.String(Value) = .init(allocator); |
| 756 | defer value_map.deinit(); |
| 771 | 757 | |
| 772 | | try values.putNoClobber("undef", .undef); |
| 773 | | try values.putNoClobber("defined", .defined); |
| 774 | | try values.putNoClobber("true", Value{ .boolean = true }); |
| 775 | | try values.putNoClobber("false", Value{ .boolean = false }); |
| 776 | | try values.putNoClobber("int", Value{ .int = 42 }); |
| 777 | | try values.putNoClobber("ident", Value{ .string = "value" }); |
| 778 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 758 | try value_map.putNoClobber("undef", .undef); |
| 759 | try value_map.putNoClobber("defined", .defined); |
| 760 | try value_map.putNoClobber("true", Value{ .boolean = true }); |
| 761 | try value_map.putNoClobber("false", Value{ .boolean = false }); |
| 762 | try value_map.putNoClobber("int", Value{ .int = 42 }); |
| 763 | try value_map.putNoClobber("ident", Value{ .string = "value" }); |
| 764 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 779 | 765 | |
| 780 | 766 | // empty strings are preserved |
| 781 | | try testReplaceVariablesCMake(allocator, "", "", values); |
| 767 | try testReplaceVariablesCMake(allocator, "", "", value_map); |
| 782 | 768 | |
| 783 | 769 | // line with misc content is preserved |
| 784 | | try testReplaceVariablesCMake(allocator, "no substitution", "no substitution", values); |
| 770 | try testReplaceVariablesCMake(allocator, "no substitution", "no substitution", value_map); |
| 785 | 771 | |
| 786 | 772 | // empty ${} wrapper leads to an error |
| 787 | | try std.testing.expectError(error.MissingKey, testReplaceVariablesCMake(allocator, "${}", "", values)); |
| 773 | try std.testing.expectError(error.MissingKey, testReplaceVariablesCMake(allocator, "${}", "", value_map)); |
| 788 | 774 | |
| 789 | 775 | // empty @ sigils are preserved |
| 790 | | try testReplaceVariablesCMake(allocator, "@", "@", values); |
| 791 | | try testReplaceVariablesCMake(allocator, "@@", "@@", values); |
| 792 | | try testReplaceVariablesCMake(allocator, "@@@", "@@@", values); |
| 793 | | try testReplaceVariablesCMake(allocator, "@@@@", "@@@@", values); |
| 776 | try testReplaceVariablesCMake(allocator, "@", "@", value_map); |
| 777 | try testReplaceVariablesCMake(allocator, "@@", "@@", value_map); |
| 778 | try testReplaceVariablesCMake(allocator, "@@@", "@@@", value_map); |
| 779 | try testReplaceVariablesCMake(allocator, "@@@@", "@@@@", value_map); |
| 794 | 780 | |
| 795 | 781 | // simple substitution |
| 796 | | try testReplaceVariablesCMake(allocator, "@undef@", "", values); |
| 797 | | try testReplaceVariablesCMake(allocator, "${undef}", "", values); |
| 798 | | try testReplaceVariablesCMake(allocator, "@defined@", "", values); |
| 799 | | try testReplaceVariablesCMake(allocator, "${defined}", "", values); |
| 800 | | try testReplaceVariablesCMake(allocator, "@true@", "1", values); |
| 801 | | try testReplaceVariablesCMake(allocator, "${true}", "1", values); |
| 802 | | try testReplaceVariablesCMake(allocator, "@false@", "0", values); |
| 803 | | try testReplaceVariablesCMake(allocator, "${false}", "0", values); |
| 804 | | try testReplaceVariablesCMake(allocator, "@int@", "42", values); |
| 805 | | try testReplaceVariablesCMake(allocator, "${int}", "42", values); |
| 806 | | try testReplaceVariablesCMake(allocator, "@ident@", "value", values); |
| 807 | | try testReplaceVariablesCMake(allocator, "${ident}", "value", values); |
| 808 | | try testReplaceVariablesCMake(allocator, "@string@", "text", values); |
| 809 | | try testReplaceVariablesCMake(allocator, "${string}", "text", values); |
| 782 | try testReplaceVariablesCMake(allocator, "@undef@", "", value_map); |
| 783 | try testReplaceVariablesCMake(allocator, "${undef}", "", value_map); |
| 784 | try testReplaceVariablesCMake(allocator, "@defined@", "", value_map); |
| 785 | try testReplaceVariablesCMake(allocator, "${defined}", "", value_map); |
| 786 | try testReplaceVariablesCMake(allocator, "@true@", "1", value_map); |
| 787 | try testReplaceVariablesCMake(allocator, "${true}", "1", value_map); |
| 788 | try testReplaceVariablesCMake(allocator, "@false@", "0", value_map); |
| 789 | try testReplaceVariablesCMake(allocator, "${false}", "0", value_map); |
| 790 | try testReplaceVariablesCMake(allocator, "@int@", "42", value_map); |
| 791 | try testReplaceVariablesCMake(allocator, "${int}", "42", value_map); |
| 792 | try testReplaceVariablesCMake(allocator, "@ident@", "value", value_map); |
| 793 | try testReplaceVariablesCMake(allocator, "${ident}", "value", value_map); |
| 794 | try testReplaceVariablesCMake(allocator, "@string@", "text", value_map); |
| 795 | try testReplaceVariablesCMake(allocator, "${string}", "text", value_map); |
| 810 | 796 | |
| 811 | 797 | // double packed substitution |
| 812 | | try testReplaceVariablesCMake(allocator, "@string@@string@", "texttext", values); |
| 813 | | try testReplaceVariablesCMake(allocator, "${string}${string}", "texttext", values); |
| 798 | try testReplaceVariablesCMake(allocator, "@string@@string@", "texttext", value_map); |
| 799 | try testReplaceVariablesCMake(allocator, "${string}${string}", "texttext", value_map); |
| 814 | 800 | |
| 815 | 801 | // triple packed substitution |
| 816 | | try testReplaceVariablesCMake(allocator, "@string@@int@@string@", "text42text", values); |
| 817 | | try testReplaceVariablesCMake(allocator, "@string@${int}@string@", "text42text", values); |
| 818 | | try testReplaceVariablesCMake(allocator, "${string}@int@${string}", "text42text", values); |
| 819 | | try testReplaceVariablesCMake(allocator, "${string}${int}${string}", "text42text", values); |
| 802 | try testReplaceVariablesCMake(allocator, "@string@@int@@string@", "text42text", value_map); |
| 803 | try testReplaceVariablesCMake(allocator, "@string@${int}@string@", "text42text", value_map); |
| 804 | try testReplaceVariablesCMake(allocator, "${string}@int@${string}", "text42text", value_map); |
| 805 | try testReplaceVariablesCMake(allocator, "${string}${int}${string}", "text42text", value_map); |
| 820 | 806 | |
| 821 | 807 | // double separated substitution |
| 822 | | try testReplaceVariablesCMake(allocator, "@int@.@int@", "42.42", values); |
| 823 | | try testReplaceVariablesCMake(allocator, "${int}.${int}", "42.42", values); |
| 808 | try testReplaceVariablesCMake(allocator, "@int@.@int@", "42.42", value_map); |
| 809 | try testReplaceVariablesCMake(allocator, "${int}.${int}", "42.42", value_map); |
| 824 | 810 | |
| 825 | 811 | // triple separated substitution |
| 826 | | try testReplaceVariablesCMake(allocator, "@int@.@true@.@int@", "42.1.42", values); |
| 827 | | try testReplaceVariablesCMake(allocator, "@int@.${true}.@int@", "42.1.42", values); |
| 828 | | try testReplaceVariablesCMake(allocator, "${int}.@true@.${int}", "42.1.42", values); |
| 829 | | try testReplaceVariablesCMake(allocator, "${int}.${true}.${int}", "42.1.42", values); |
| 812 | try testReplaceVariablesCMake(allocator, "@int@.@true@.@int@", "42.1.42", value_map); |
| 813 | try testReplaceVariablesCMake(allocator, "@int@.${true}.@int@", "42.1.42", value_map); |
| 814 | try testReplaceVariablesCMake(allocator, "${int}.@true@.${int}", "42.1.42", value_map); |
| 815 | try testReplaceVariablesCMake(allocator, "${int}.${true}.${int}", "42.1.42", value_map); |
| 830 | 816 | |
| 831 | 817 | // misc prefix is preserved |
| 832 | | try testReplaceVariablesCMake(allocator, "false is @false@", "false is 0", values); |
| 833 | | try testReplaceVariablesCMake(allocator, "false is ${false}", "false is 0", values); |
| 818 | try testReplaceVariablesCMake(allocator, "false is @false@", "false is 0", value_map); |
| 819 | try testReplaceVariablesCMake(allocator, "false is ${false}", "false is 0", value_map); |
| 834 | 820 | |
| 835 | 821 | // misc suffix is preserved |
| 836 | | try testReplaceVariablesCMake(allocator, "@true@ is true", "1 is true", values); |
| 837 | | try testReplaceVariablesCMake(allocator, "${true} is true", "1 is true", values); |
| 822 | try testReplaceVariablesCMake(allocator, "@true@ is true", "1 is true", value_map); |
| 823 | try testReplaceVariablesCMake(allocator, "${true} is true", "1 is true", value_map); |
| 838 | 824 | |
| 839 | 825 | // surrounding content is preserved |
| 840 | | try testReplaceVariablesCMake(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", values); |
| 841 | | try testReplaceVariablesCMake(allocator, "what is 6*7? ${int}!", "what is 6*7? 42!", values); |
| 826 | try testReplaceVariablesCMake(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", value_map); |
| 827 | try testReplaceVariablesCMake(allocator, "what is 6*7? ${int}!", "what is 6*7? 42!", value_map); |
| 842 | 828 | |
| 843 | 829 | // incomplete key is preserved |
| 844 | | try testReplaceVariablesCMake(allocator, "@undef", "@undef", values); |
| 845 | | try testReplaceVariablesCMake(allocator, "${undef", "${undef", values); |
| 846 | | try testReplaceVariablesCMake(allocator, "{undef}", "{undef}", values); |
| 847 | | try testReplaceVariablesCMake(allocator, "undef@", "undef@", values); |
| 848 | | try testReplaceVariablesCMake(allocator, "undef}", "undef}", values); |
| 830 | try testReplaceVariablesCMake(allocator, "@undef", "@undef", value_map); |
| 831 | try testReplaceVariablesCMake(allocator, "${undef", "${undef", value_map); |
| 832 | try testReplaceVariablesCMake(allocator, "{undef}", "{undef}", value_map); |
| 833 | try testReplaceVariablesCMake(allocator, "undef@", "undef@", value_map); |
| 834 | try testReplaceVariablesCMake(allocator, "undef}", "undef}", value_map); |
| 849 | 835 | |
| 850 | 836 | // unknown key leads to an error |
| 851 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@bad@", "", values)); |
| 852 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", values)); |
| 837 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@bad@", "", value_map)); |
| 838 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", value_map)); |
| 853 | 839 | } |
| 854 | 840 | |
| 855 | 841 | test "expandVariablesCmake edge cases" { |
| 856 | 842 | const allocator = std.testing.allocator; |
| 857 | | var values: std.array_hash_map.String(Value) = .init(allocator); |
| 858 | | defer values.deinit(); |
| 843 | var value_map: std.array_hash_map.String(Value) = .init(allocator); |
| 844 | defer value_map.deinit(); |
| 859 | 845 | |
| 860 | 846 | // special symbols |
| 861 | | try values.putNoClobber("at", Value{ .string = "@" }); |
| 862 | | try values.putNoClobber("dollar", Value{ .string = "$" }); |
| 863 | | try values.putNoClobber("underscore", Value{ .string = "_" }); |
| 847 | try value_map.putNoClobber("at", Value{ .string = "@" }); |
| 848 | try value_map.putNoClobber("dollar", Value{ .string = "$" }); |
| 849 | try value_map.putNoClobber("underscore", Value{ .string = "_" }); |
| 864 | 850 | |
| 865 | 851 | // basic value |
| 866 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 852 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 867 | 853 | |
| 868 | | // proxy case values |
| 869 | | try values.putNoClobber("string_proxy", Value{ .string = "string" }); |
| 870 | | try values.putNoClobber("string_at", Value{ .string = "@string@" }); |
| 871 | | try values.putNoClobber("string_curly", Value{ .string = "{string}" }); |
| 872 | | try values.putNoClobber("string_var", Value{ .string = "${string}" }); |
| 854 | // proxy case value_map |
| 855 | try value_map.putNoClobber("string_proxy", Value{ .string = "string" }); |
| 856 | try value_map.putNoClobber("string_at", Value{ .string = "@string@" }); |
| 857 | try value_map.putNoClobber("string_curly", Value{ .string = "{string}" }); |
| 858 | try value_map.putNoClobber("string_var", Value{ .string = "${string}" }); |
| 873 | 859 | |
| 874 | | // stack case values |
| 875 | | try values.putNoClobber("nest_underscore_proxy", Value{ .string = "underscore" }); |
| 876 | | try values.putNoClobber("nest_proxy", Value{ .string = "nest_underscore_proxy" }); |
| 860 | // stack case value_map |
| 861 | try value_map.putNoClobber("nest_underscore_proxy", Value{ .string = "underscore" }); |
| 862 | try value_map.putNoClobber("nest_proxy", Value{ .string = "nest_underscore_proxy" }); |
| 877 | 863 | |
| 878 | 864 | // @-vars resolved only when they wrap valid characters, otherwise considered literals |
| 879 | | try testReplaceVariablesCMake(allocator, "@@string@@", "@text@", values); |
| 880 | | try testReplaceVariablesCMake(allocator, "@${string}@", "@text@", values); |
| 865 | try testReplaceVariablesCMake(allocator, "@@string@@", "@text@", value_map); |
| 866 | try testReplaceVariablesCMake(allocator, "@${string}@", "@text@", value_map); |
| 881 | 867 | |
| 882 | 868 | // @-vars are resolved inside ${}-vars |
| 883 | | try testReplaceVariablesCMake(allocator, "${@string_proxy@}", "text", values); |
| 869 | try testReplaceVariablesCMake(allocator, "${@string_proxy@}", "text", value_map); |
| 884 | 870 | |
| 885 | 871 | // expanded variables are considered strings after expansion |
| 886 | | try testReplaceVariablesCMake(allocator, "@string_at@", "@string@", values); |
| 887 | | try testReplaceVariablesCMake(allocator, "${string_at}", "@string@", values); |
| 888 | | try testReplaceVariablesCMake(allocator, "$@string_curly@", "${string}", values); |
| 889 | | try testReplaceVariablesCMake(allocator, "$${string_curly}", "${string}", values); |
| 890 | | try testReplaceVariablesCMake(allocator, "${string_var}", "${string}", values); |
| 891 | | try testReplaceVariablesCMake(allocator, "@string_var@", "${string}", values); |
| 892 | | try testReplaceVariablesCMake(allocator, "${dollar}{${string}}", "${text}", values); |
| 893 | | try testReplaceVariablesCMake(allocator, "@dollar@{${string}}", "${text}", values); |
| 894 | | try testReplaceVariablesCMake(allocator, "@dollar@{@string@}", "${text}", values); |
| 872 | try testReplaceVariablesCMake(allocator, "@string_at@", "@string@", value_map); |
| 873 | try testReplaceVariablesCMake(allocator, "${string_at}", "@string@", value_map); |
| 874 | try testReplaceVariablesCMake(allocator, "$@string_curly@", "${string}", value_map); |
| 875 | try testReplaceVariablesCMake(allocator, "$${string_curly}", "${string}", value_map); |
| 876 | try testReplaceVariablesCMake(allocator, "${string_var}", "${string}", value_map); |
| 877 | try testReplaceVariablesCMake(allocator, "@string_var@", "${string}", value_map); |
| 878 | try testReplaceVariablesCMake(allocator, "${dollar}{${string}}", "${text}", value_map); |
| 879 | try testReplaceVariablesCMake(allocator, "@dollar@{${string}}", "${text}", value_map); |
| 880 | try testReplaceVariablesCMake(allocator, "@dollar@{@string@}", "${text}", value_map); |
| 895 | 881 | |
| 896 | 882 | // when expanded variables contain invalid characters, they prevent further expansion |
| 897 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${${string_var}}", "", values)); |
| 898 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${@string_var@}", "", values)); |
| 883 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${${string_var}}", "", value_map)); |
| 884 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${@string_var@}", "", value_map)); |
| 899 | 885 | |
| 900 | 886 | // nested expanded variables are expanded from the inside out |
| 901 | | try testReplaceVariablesCMake(allocator, "${string${underscore}proxy}", "string", values); |
| 902 | | try testReplaceVariablesCMake(allocator, "${string@underscore@proxy}", "string", values); |
| 887 | try testReplaceVariablesCMake(allocator, "${string${underscore}proxy}", "string", value_map); |
| 888 | try testReplaceVariablesCMake(allocator, "${string@underscore@proxy}", "string", value_map); |
| 903 | 889 | |
| 904 | 890 | // nested vars are only expanded when ${} is closed |
| 905 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@underscore@proxy@", "", values)); |
| 906 | | try testReplaceVariablesCMake(allocator, "${nest${underscore}proxy}", "nest_underscore_proxy", values); |
| 907 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@@nest_underscore@underscore@proxy@@proxy@", "", values)); |
| 908 | | try testReplaceVariablesCMake(allocator, "${nest${${nest_underscore${underscore}proxy}}proxy}", "nest_underscore_proxy", values); |
| 891 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@underscore@proxy@", "", value_map)); |
| 892 | try testReplaceVariablesCMake(allocator, "${nest${underscore}proxy}", "nest_underscore_proxy", value_map); |
| 893 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@@nest_underscore@underscore@proxy@@proxy@", "", value_map)); |
| 894 | try testReplaceVariablesCMake(allocator, "${nest${${nest_underscore${underscore}proxy}}proxy}", "nest_underscore_proxy", value_map); |
| 909 | 895 | |
| 910 | 896 | // invalid characters lead to an error |
| 911 | | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str*ing}", "", values)); |
| 912 | | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str$ing}", "", values)); |
| 913 | | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", values)); |
| 897 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str*ing}", "", value_map)); |
| 898 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str$ing}", "", value_map)); |
| 899 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", value_map)); |
| 914 | 900 | } |
| 915 | 901 | |
| 916 | 902 | test "expandVariablesCmake escaped characters" { |
| 917 | 903 | const allocator = std.testing.allocator; |
| 918 | | var values: std.array_hash_map.String(Value) = .init(allocator); |
| 919 | | defer values.deinit(); |
| 904 | var value_map: std.array_hash_map.String(Value) = .init(allocator); |
| 905 | defer value_map.deinit(); |
| 920 | 906 | |
| 921 | | try values.putNoClobber("string", Value{ .string = "text" }); |
| 907 | try value_map.putNoClobber("string", Value{ .string = "text" }); |
| 922 | 908 | |
| 923 | 909 | // backslash is an invalid character for @ lookup |
| 924 | | try testReplaceVariablesCMake(allocator, "\\@string\\@", "\\@string\\@", values); |
| 910 | try testReplaceVariablesCMake(allocator, "\\@string\\@", "\\@string\\@", value_map); |
| 925 | 911 | |
| 926 | 912 | // backslash is preserved, but doesn't affect ${} variable expansion |
| 927 | | try testReplaceVariablesCMake(allocator, "\\${string}", "\\text", values); |
| 913 | try testReplaceVariablesCMake(allocator, "\\${string}", "\\text", value_map); |
| 928 | 914 | |
| 929 | 915 | // backslash breaks ${} opening bracket identification |
| 930 | | try testReplaceVariablesCMake(allocator, "$\\{string}", "$\\{string}", values); |
| 916 | try testReplaceVariablesCMake(allocator, "$\\{string}", "$\\{string}", value_map); |
| 931 | 917 | |
| 932 | 918 | // backslash is skipped when checking for invalid characters, yet it mangles the key |
| 933 | | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${string\\}", "", values)); |
| 919 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${string\\}", "", value_map)); |
| 934 | 920 | } |