From b28026081793525db9dbed0edc27c6f16698bd2f Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Sun, 3 Aug 2025 00:08:30 +0300 Subject: [PATCH 1/2] source/external/ldc/config.d: Backport upstream ~= operator addition Since version 1.41.0 the official ldc2 packages for macos started using this operator in the configuration files which makes serve-d fail to initialize. Bug: https://github.com/dlang-community/DCD/issues/787 See-Also: https://github.com/ldc-developers/ldc/pull/4856 Signed-off-by: Andrei Horodniceanu --- source/external/ldc/config.d | 115 +++++++++++++++++++--------- source/served/utils/stdlib_detect.d | 6 ++ 2 files changed, 85 insertions(+), 36 deletions(-) diff --git a/source/external/ldc/config.d b/source/external/ldc/config.d index 5d16829d..d33492ba 100644 --- a/source/external/ldc/config.d +++ b/source/external/ldc/config.d @@ -70,10 +70,11 @@ class ScalarSetting : Setting class ArraySetting : Setting { - this(string name, string[] vals) + this(string name, string[] vals, bool isAppending) { super(name, Type.array); _vals = vals; + _isAppending = isAppending; } @property const(string)[] vals() const @@ -81,7 +82,13 @@ class ArraySetting : Setting return _vals; } + @property bool isAppending() const + { + return _isAppending; + } + private string[] _vals; + private bool _isAppending; } class GroupSetting : Setting @@ -125,7 +132,7 @@ EBNF grammar. It is a subset of the libconfig grammar (http://www.hyperrealm.com/libconfig). config = { ows , setting } , ows ; -setting = (name | string) , (":" | "=") , value , [";" | ","] ; +setting = (name | string) , (":" | "=" | "~=") , value , [";" | ","] ; name = alpha , { alpha | digit | "_" | "-" } ; value = string | array | group ; array = "[" , ows , @@ -164,6 +171,7 @@ enum Token { name, assign, // ':' or '=' + appendAssign, // '~=' str, lbrace, // '{' rbrace, // '}' @@ -179,17 +187,18 @@ string humanReadableToken(in Token tok) { final switch(tok) { - case Token.name: return `"name"`; - case Token.assign: return `':' or '='`; - case Token.str: return `"string"`; - case Token.lbrace: return `'{'`; - case Token.rbrace: return `'}'`; - case Token.lbracket: return `'['`; - case Token.rbracket: return `']'`; - case Token.semicolon: return `';'`; - case Token.comma: return `','`; - case Token.unknown: return `"unknown token"`; - case Token.eof: return `"end of file"`; + case Token.name: return `"name"`; + case Token.assign: return `':' or '='`; + case Token.appendAssign: return `'~='`; + case Token.str: return `"string"`; + case Token.lbrace: return `'{'`; + case Token.rbrace: return `'}'`; + case Token.lbracket: return `'['`; + case Token.rbracket: return `']'`; + case Token.semicolon: return `';'`; + case Token.comma: return `','`; + case Token.unknown: return `"unknown token"`; + case Token.eof: return `"end of file"`; } } @@ -218,11 +227,14 @@ struct Parser void error(in string msg) { - enum fmt = "Error while reading config file: %.*s\nline %d: %.*s"; - char[1024] buf; - auto len = snprintf(buf.ptr, buf.length, fmt, cast(int) filename.length, - filename.ptr, lineNum, cast(int) msg.length, msg.ptr); - throw new Exception(buf[0 .. len].idup); + error(msg, lineNum); + } + + void error(in string msg, int lineNum) + { + char[20] buf = void; + auto len = snprintf(buf.ptr, buf.length, "line %d: ", lineNum); + throw new Exception((cast(string) buf[0 .. len]) ~ msg); } char getChar() @@ -267,6 +279,19 @@ struct Parser return getTok(outStr); } + if (lastChar == '~') + { + lastChar = getChar(); + if (lastChar != '=') + { + outStr = "~"; + return Token.unknown; + } + + lastChar = getChar(); + return Token.appendAssign; + } + if (isalpha(lastChar)) { string name; @@ -402,17 +427,6 @@ struct Parser ". Got " ~ humanReadableToken(tok) ~ s ~ " instead."); } - string accept(in Token expected) - { - string s; - immutable tok = getTok(s); - if (tok != expected) - { - unexpectedTokenError(tok, expected, s); - } - return s; - } - Setting[] parseConfig() { Setting[] res; @@ -442,11 +456,29 @@ struct Parser assert(false); } - accept(Token.assign); + string s; + t = getTok(s); + if (t != Token.assign && t != Token.appendAssign) + { + auto msg = "Expected either" + ~ " token " ~ humanReadableToken(Token.assign) + ~ " or token " ~ humanReadableToken(Token.appendAssign) + ~ " but got: " ~ humanReadableToken(t) + ~ ' ' ~ (s.length ? '(' ~ s ~ ')' : s); + error(msg); + } + // This is off by +1 if `t` is followed by \n + const assignLineNum = lineNum; - Setting res = parseValue(name); + Setting res = parseValue(name, t); + if (t == Token.appendAssign) + { + if (res.type == Setting.Type.scalar) + error(humanReadableToken(t) ~ " is not supported with scalar values", assignLineNum); + if (res.type == Setting.Type.group) + error(humanReadableToken(t) ~ " is not supported with groups", assignLineNum); + } - string s; t = getTok(s); if (t != Token.semicolon && t != Token.comma) { @@ -456,8 +488,10 @@ struct Parser return res; } - Setting parseValue(string name) + Setting parseValue(string name, Token tAssign = Token.assign) { + assert(tAssign == Token.assign || tAssign == Token.appendAssign); + string s; auto t = getTok(s); if (t == Token.str) @@ -466,6 +500,7 @@ struct Parser } else if (t == Token.lbracket) { + const isAppending = tAssign == Token.appendAssign; string[] arrVal; while (1) { @@ -477,7 +512,7 @@ struct Parser arrVal ~= s; break; case Token.rbracket: - return new ArraySetting(name, arrVal); + return new ArraySetting(name, arrVal, isAppending); default: unexpectedTokenError(t, Token.str, s); assert(false); @@ -490,7 +525,7 @@ struct Parser case Token.comma: break; case Token.rbracket: - return new ArraySetting(name, arrVal); + return new ArraySetting(name, arrVal, isAppending); default: unexpectedTokenError(t, Token.comma, s); assert(false); @@ -570,6 +605,8 @@ group-1_2: {}; scalar = "abc"; // comment Array_1-2 = [ "a" ]; + + AppArray ~= [ "x" ]; // appending array }; `; @@ -583,7 +620,7 @@ group-1_2: {}; assert(settings[1].name == "86(_64)?-.*linux\\.?"); assert(settings[1].type == Setting.Type.group); auto group2 = cast(GroupSetting) settings[1]; - assert(group2.children.length == 2); + assert(group2.children.length == 3); assert(group2.children[0].name == "scalar"); assert(group2.children[0].type == Setting.Type.scalar); @@ -592,4 +629,10 @@ group-1_2: {}; assert(group2.children[1].name == "Array_1-2"); assert(group2.children[1].type == Setting.Type.array); assert((cast(ArraySetting) group2.children[1]).vals == [ "a" ]); + assert((cast(ArraySetting) group2.children[1]).isAppending == false); + + assert(group2.children[2].name == "AppArray"); + assert(group2.children[2].type == Setting.Type.array); + assert((cast(ArraySetting) group2.children[2]).vals == [ "x" ]); + assert((cast(ArraySetting) group2.children[2]).isAppending == true); } diff --git a/source/served/utils/stdlib_detect.d b/source/served/utils/stdlib_detect.d index e523fb02..f9d81ddf 100644 --- a/source/served/utils/stdlib_detect.d +++ b/source/served/utils/stdlib_detect.d @@ -388,6 +388,12 @@ bool parseLdcConfImports(string confPath, scope const(char)[] binDirPath, out st } trace("test ldc conf ", confPath); + Setting[] settings; + try + settings = parseConfigFile(confPath); + catch (Exception e) + throw new Exception("Could not read ldc2 config file: " ~ confPath ~ ": " ~ e.msg); + foreach (s; parseConfigFile(confPath)) { if (s.type == Setting.Type.group && s.name == "default") From 416596ad1ca30ba702774e647dfe1aaf7dde33ad Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Sun, 3 Aug 2025 01:33:52 +0300 Subject: [PATCH 2/2] utils/stdlib_detect: Support directory form of ldc2.conf See-Also: https://github.com/ldc-developers/ldc/pull/4954 Signed-off-by: Andrei Horodniceanu --- source/served/utils/stdlib_detect.d | 41 +++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/source/served/utils/stdlib_detect.d b/source/served/utils/stdlib_detect.d index f9d81ddf..0e2b77f3 100644 --- a/source/served/utils/stdlib_detect.d +++ b/source/served/utils/stdlib_detect.d @@ -1,7 +1,7 @@ module served.utils.stdlib_detect; import std.algorithm : countUntil, splitter, startsWith; -import std.array : appender, replace; +import std.array : Appender, appender, replace; import std.ascii : isWhite; import std.conv : to; import std.experimental.logger : trace, warning; @@ -359,9 +359,39 @@ bool parseDmdConfImports(R)(R confPath, scope const(char)[] confDirPath, out str bool parseLdcConfImports(string confPath, scope const(char)[] binDirPath, out string[] paths) { - import external.ldc.config; + Appender!(string[]) ret; + scope(exit) paths = ret.data; - auto ret = appender!(string[]); + import std.algorithm; + import std.array; + import std.file; + + if (confPath.isDir) + { + auto configFiles = confPath.dirEntries(SpanMode.shallow) + .filter!`a.isFile` + .map!`a.name` + // FIXME this should be numeric sort + .array + .sort; + + import std.conv; + trace("the config files are: ", text(configFiles)); + + foreach (file; configFiles) + parseLdcConfImportsSingleFile(file, binDirPath, ret); + } + else + parseLdcConfImportsSingleFile(confPath, binDirPath, ret); + + if (!ret.data.length) + warning("failed to find phobos/druntime paths in ldc conf ", confPath, " - going to continue looking elsewhere..."); + return ret.data.length > 0; +} + +void parseLdcConfImportsSingleFile(string confPath, scope const(char)[] binDirPath, ref Appender!(string[]) ret) +{ + import external.ldc.config; binDirPath = binDirPath.replace('\\', '/'); @@ -401,11 +431,6 @@ bool parseLdcConfImports(string confPath, scope const(char)[] binDirPath, out st parseSection(cast(GroupSetting) s); } } - - paths = ret.data; - if (!ret.data.length) - warning("failed to find phobos/druntime paths in ldc conf ", confPath, " - going to continue looking elsewhere..."); - return ret.data.length > 0; } deprecated string[] parseDflagsImports(scope const(char)[] options, bool windows)