diff --git a/driver/config.d b/driver/config.d
index 46990a49a93..a829c6d13aa 100644
--- a/driver/config.d
+++ b/driver/config.d
@@ -66,10 +66,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
@@ -77,7 +78,13 @@ class ArraySetting : Setting
         return _vals;
     }
 
+    @property bool isAppending() const
+    {
+        return _isAppending;
+    }
+
     private string[] _vals;
+    private bool _isAppending;
 }
 
 class GroupSetting : Setting
@@ -133,7 +140,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 ,
@@ -172,6 +179,7 @@ enum Token
 {
     name,
     assign,         // ':' or '='
+    appendAssign,   // '~='
     str,
     lbrace,         // '{'
     rbrace,         // '}'
@@ -187,17 +195,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"`;
     }
 }
 
@@ -226,10 +235,15 @@ struct Parser
 
     void error(in string msg)
     {
-        enum fmt = "Error while reading config file: %.*s\nline %d: %.*s";
+        error(msg, lineNum);
+    }
+
+    void error(in string msg, int lineNum)
+    {
+        enum fmt = "line %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);
+        auto len = snprintf(buf.ptr, buf.length, fmt,
+                            lineNum, cast(int) msg.length, msg.ptr);
         throw new Exception(buf[0 .. len].idup);
     }
 
@@ -275,6 +289,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;
@@ -410,17 +437,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;
@@ -450,11 +466,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)
         {
@@ -464,8 +498,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)
@@ -474,6 +510,7 @@ struct Parser
         }
         else if (t == Token.lbracket)
         {
+            const isAppending = tAssign == Token.appendAssign;
             string[] arrVal;
             while (1)
             {
@@ -485,7 +522,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);
@@ -498,7 +535,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);
@@ -578,6 +615,8 @@ group-1_2: {};
     scalar = "abc";
     // comment
     Array_1-2 = [ "a" ];
+
+    AppArray ~= [ "x" ]; // appending array
 };
 `;
 
@@ -591,7 +630,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);
@@ -600,4 +639,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/driver/configfile.d b/driver/configfile.d
index c2be290afc2..f77b17eb04f 100644
--- a/driver/configfile.d
+++ b/driver/configfile.d
@@ -30,28 +30,42 @@ string normalizeSlashes(const(char)* binDir)
     return cast(string)res; // assumeUnique
 }
 
-T findSetting(T)(GroupSetting[] sections, Setting.Type type, string name)
+T findSetting(T, alias reducer)(GroupSetting[] sections, Setting.Type type, string name)
 {
-    // lexically later sections dominate earlier ones
-    foreach_reverse (section; sections)
+    T result = null;
+    foreach (section; sections)
     {
         foreach (c; section.children)
         {
             if (c.type == type && c.name == name)
-                return cast(T) c;
+            {
+                if (result !is null)
+                    result = reducer(result, cast(T) c);
+                else
+                    result = cast(T) c;
+            }
         }
     }
-    return null;
+    return result;
 }
 
 ArraySetting findArraySetting(GroupSetting[] sections, string name)
 {
-    return findSetting!ArraySetting(sections, Setting.Type.array, name);
+    auto merge(ArraySetting a, ArraySetting b)
+    {
+        string[] newVals;
+        if (b.isAppending)
+            newVals = [] ~ a.vals ~ b.vals;
+        else
+            newVals = [] ~ b.vals;
+        return new ArraySetting(a.name, newVals, a.isAppending && b.isAppending);
+    }
+    return findSetting!(ArraySetting, merge)(sections, Setting.Type.array, name);
 }
 
 ScalarSetting findScalarSetting(GroupSetting[] sections, string name)
 {
-    return findSetting!ScalarSetting(sections, Setting.Type.scalar, name);
+    return findSetting!(ScalarSetting, (o, n) => n)(sections, Setting.Type.scalar, name);
 }
 
 string replace(string str, string pattern, string replacement)
@@ -137,8 +151,6 @@ private:
 
     bool readConfig(const(char)* cfPath, const(char)* triple, const(char)* binDir)
     {
-        switches.setDim(0);
-        postSwitches.setDim(0);
         const cfgPaths = CfgPaths(cfPath, binDir);
 
         try
@@ -156,24 +168,23 @@ private:
             if (sections.length == 0)
             {
                 const dTriple = triple[0 .. strlen(triple)];
-                const dCfPath = cfPath[0 .. strlen(cfPath)];
                 throw new Exception("No matching section for triple '" ~ cast(string) dTriple
-                                    ~ "' in " ~ cast(string) dCfPath);
+                                    ~ "'");
             }
 
             auto switches = findArraySetting(sections, "switches");
             auto postSwitches = findArraySetting(sections, "post-switches");
             if (!switches && !postSwitches)
-            {
-                const dCfPath = cfPath[0 .. strlen(cfPath)];
-                throw new Exception("Could not look up switches in " ~ cast(string) dCfPath);
-            }
+                throw new Exception("Could not look up switches");
 
             void applyArray(ref Array!(const(char)*) output, ArraySetting input)
             {
                 if (!input)
                     return;
 
+                if (!input.isAppending)
+                    output.setDim(0);
+
                 output.reserve(input.vals.length);
                 foreach (sw; input.vals)
                 {
@@ -195,7 +206,7 @@ private:
         }
         catch (Exception ex)
         {
-            fprintf(stderr, "Error: %.*s\n", cast(int) ex.msg.length, ex.msg.ptr);
+            fprintf(stderr, "Error while reading config file: %s\n%.*s\n", cfPath, cast(int) ex.msg.length, ex.msg.ptr);
             return false;
         }
     }
diff --git a/tests/driver/config_append_assign.d b/tests/driver/config_append_assign.d
new file mode 100644
index 00000000000..6ff31defa47
--- /dev/null
+++ b/tests/driver/config_append_assign.d
@@ -0,0 +1,11 @@
+// RUN: %ldc -o- -v -conf=%S/inputs/appending_assign.conf %s 2>&1
+
+module object;
+
+version(Section1_1)
+static assert(false);
+version(Section1_2) {}
+else static assert(false);
+
+version(Section2) {}
+else static assert(false);
diff --git a/tests/driver/config_diag.d b/tests/driver/config_diag.d
index 31cebd3cd7a..eae17e18c0a 100644
--- a/tests/driver/config_diag.d
+++ b/tests/driver/config_diag.d
@@ -1,10 +1,13 @@
-// RUN: not %ldc -conf=%S/inputs/noswitches.conf %s 2>&1 | FileCheck %s --check-prefix=NOSWITCHES
-// NOSWITCHES: Could not look up switches in {{.*}}noswitches.conf
+// RUN: %ldc -o- -conf=%S/inputs/noswitches.conf %s 2>&1 | FileCheck %s --check-prefix=NOSWITCHES
+// NOSWITCHES: Error while reading config file: {{.*}}noswitches.conf
+// NOSWITCHES-NEXT: Could not look up switches
 
-// RUN: not %ldc -conf=%S/inputs/section_aaa.conf %s 2>&1 | FileCheck %s --check-prefix=NO_SEC
-// NO_SEC: No matching section for triple '{{.*}}' in {{.*}}section_aaa.conf
+// RUN: %ldc -o- -conf=%S/inputs/section_aaa.conf %s 2>&1 | FileCheck %s --check-prefix=NO_SEC
+// NO_SEC: Error while reading config file: {{.*}}section_aaa.conf
+// NO_SEC-NEXT: No matching section for triple '{{.*}}'
 
+// RUN: %ldc -o- -conf=%S/inputs/invalid_append.conf %s 2>&1 | FileCheck %s --check-prefix=APP
+// APP: Error while reading config file: {{.*}}invalid_append.conf
+// APP-NEXT: line 3: '~=' is not supported with scalar values
 
-void foo()
-{
-}
+module object;
diff --git a/tests/driver/inputs/appending_assign.conf b/tests/driver/inputs/appending_assign.conf
new file mode 100644
index 00000000000..67a857761bd
--- /dev/null
+++ b/tests/driver/inputs/appending_assign.conf
@@ -0,0 +1,8 @@
+default: {
+	switches = [ "-d-version=Section1_1" ]
+	switches = [ "-d-version=Section1_2" ]
+}
+
+".?": {
+	switches ~= [ "-d-version=Section2" ]
+}
diff --git a/tests/driver/inputs/invalid_append.conf b/tests/driver/inputs/invalid_append.conf
new file mode 100644
index 00000000000..bca0b67c38f
--- /dev/null
+++ b/tests/driver/inputs/invalid_append.conf
@@ -0,0 +1,10 @@
+default:
+{
+	rpath ~= "/path";
+}
+
+default:
+{
+	switches = [];
+	post-switches = [];
+}