aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-06-20 20:33:52 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-06-21 13:09:10 +0300
commit5c186f901ea7d1b09ab551a3fd2a6c1fd2426d59 (patch)
treeaa41f235d3ef632c481390745bc0d589bd9c554e
parent4ef51955f2c0c2da17bb922d52fd39eae0bf4464 (diff)
Add support for build configuration warning-detecting regexes
-rw-r--r--libbbot/build-config.cxx24
-rw-r--r--libbbot/build-config.hxx6
-rw-r--r--libbbot/manifest.cxx150
-rw-r--r--libbbot/manifest.hxx21
-rw-r--r--tests/manifest/task.test32
5 files changed, 176 insertions, 57 deletions
diff --git a/libbbot/build-config.cxx b/libbbot/build-config.cxx
index 071ad7f..f18c48a 100644
--- a/libbbot/build-config.cxx
+++ b/libbbot/build-config.cxx
@@ -75,14 +75,15 @@ namespace bbot
continue;
}
- // If the third field doesn't contain '=' character, then we will treat
- // it as a target.
+ // If the third field doesn't start with '~' character and doesn't
+ // contain '=' character, then we will treat it as a target.
//
- if (tl[i].value.find ('=') == string::npos)
+ const string& v (tl[i].value);
+ if (v[0] != '~' && v.find ('=') == string::npos)
{
try
{
- config.target = target_triplet (tl[i].value);
+ config.target = target_triplet (v);
}
catch (const invalid_argument& e)
{
@@ -96,8 +97,19 @@ namespace bbot
{
for (; i < n; ++i)
{
- task_manifest::check_config (tl[i].value);
- config.vars.emplace_back (move (tl[i].value));
+ string& v (tl[i].value);
+
+ if (v[0] == '~') // Regular expression.
+ {
+ string re (v, 1);
+ task_manifest::check_regex (re);
+ config.warning_regexes.emplace_back (move (re));
+ }
+ else // Configuration variable.
+ {
+ task_manifest::check_config (v);
+ config.vars.emplace_back (move (v));
+ }
}
}
catch (const invalid_argument& e)
diff --git a/libbbot/build-config.hxx b/libbbot/build-config.hxx
index 2e96e03..363b889 100644
--- a/libbbot/build-config.hxx
+++ b/libbbot/build-config.hxx
@@ -30,6 +30,10 @@ namespace bbot
butl::optional<butl::target_triplet> target;
std::vector<std::string> vars;
+
+ // Warning-detecting regular expressions.
+ //
+ std::vector<std::string> warning_regexes;
};
using build_configs = std::vector<build_config>;
@@ -39,7 +43,7 @@ namespace bbot
//
// buildtab consists of lines in the following format:
//
- // <machine-name-pattern> <config-name> [<target>] [<config-vars>]
+ // <machine-pattern> <config> [<target>] [<config-vars>] [<warning-regex>]
//
using butl::tab_parsing;
diff --git a/libbbot/manifest.cxx b/libbbot/manifest.cxx
index 56c5f1d..dae6e3e 100644
--- a/libbbot/manifest.cxx
+++ b/libbbot/manifest.cxx
@@ -4,6 +4,7 @@
#include <libbbot/manifest.hxx>
+#include <regex>
#include <vector>
#include <string>
#include <cctype> // isxdigit()
@@ -14,6 +15,7 @@
#include <cstdint> // uint64_t
#include <stdexcept> // invalid_argument
+#include <libbutl/regex.hxx>
#include <libbutl/base64.hxx>
#include <libbutl/utility.hxx> // digit()
#include <libbutl/tab-parser.hxx>
@@ -33,8 +35,6 @@ namespace bbot
using serialization = manifest_serialization;
using name_value = manifest_name_value;
- using strings = vector<string>;
-
// result_status
//
string
@@ -390,6 +390,57 @@ namespace bbot
if (nv.value != "1")
bad_value ("unsupported format version");
+ // Parse value represented as a whitespace-separated list of quoted
+ // strings (quoting is validated and preserved) and validate each string
+ // with the function specified.
+ //
+ auto parse_tab = [&bad_value] (
+ const string& value,
+ const function<void (const string&)>& check,
+ const string& what) -> strings
+ {
+ strings r;
+
+ // Note that when reporting errors we combine the manifest value
+ // position with the respective field and error positions.
+ //
+ try
+ {
+ istringstream is (value);
+ tab_parser parser (is, "");
+
+ // Here we naturally support multiline value manifest.
+ //
+ tab_fields tl;
+ while (!(tl = parser.next ()).empty ())
+ {
+ for (auto& tf: tl)
+ {
+ try
+ {
+ check (tf.value);
+ }
+ catch (const invalid_argument& e)
+ {
+ bad_value (string ("invalid task ") + what + ": " + e.what (),
+ tf.column - 1,
+ tl.line - 1);
+ }
+
+ r.emplace_back (move (tf.value));
+ }
+ }
+ }
+ catch (const tab_parsing& e)
+ {
+ bad_value ("invalid task " + what + ": " + e.description,
+ e.column - 1,
+ e.line - 1);
+ }
+
+ return r;
+ };
+
// Parse the task manifest.
//
for (nv = p.next (); !nv.empty (); nv = p.next ())
@@ -483,46 +534,21 @@ namespace bbot
if (!config.empty ())
bad_name ("task configuration redefinition");
- // Note that when reporting errors we combine the manifest value
- // position with the respective field and error positions.
- //
- try
- {
- istringstream is (v);
- tab_parser parser (is, "");
-
- // Here we naturally support multiline config manifest.
- //
- tab_fields tl;
- while (!(tl = parser.next ()).empty ())
- {
- for (auto& tf: tl)
- {
- try
- {
- check_config (tf.value);
- }
- catch (const invalid_argument& e)
- {
- bad_value (string ("invalid task configuration: ") + e.what (),
- tf.column - 1,
- tl.line - 1);
- }
-
- config.emplace_back (move (tf.value));
- }
- }
- }
- catch (const tab_parsing& e)
- {
- bad_value ("invalid task configuration: " + e.description,
- e.column - 1,
- e.line - 1);
- }
+ config = parse_tab (v, check_config, "configuration");
if (config.empty ())
bad_value ("empty task configuration");
}
+ else if (n == "warning-regex")
+ {
+ if (!warning_regex.empty ())
+ bad_name ("task warning regex redefinition");
+
+ warning_regex = parse_tab (v, check_regex, "warning regex");
+
+ if (warning_regex.empty ())
+ bad_value ("empty task warning regex");
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in task manifest");
}
@@ -561,21 +587,28 @@ namespace bbot
if (target)
s.next ("target", target->string ());
- // Recompose config string as a space-separated variable list,
+ // Serialize an optional value of the strings type as a space-separated
+ // string list.
//
- if (!config.empty ())
+ auto serialize_list = [&s] (const char* name, const strings& value)
{
- string v;
- for (auto b (config.cbegin ()), i (b), e (config.cend ()); i != e; ++i)
+ if (!value.empty ())
{
- if (i != b)
- v += ' ';
+ string v;
+ for (auto b (value.cbegin ()), i (b), e (value.cend ()); i != e; ++i)
+ {
+ if (i != b)
+ v += ' ';
- v += *i;
+ v += *i;
+ }
+
+ s.next (name, v);
}
+ };
- s.next ("config", v);
- }
+ serialize_list ("config", config);
+ serialize_list ("warning-regex", warning_regex);
s.next ("", ""); // End of manifest.
}
@@ -586,6 +619,12 @@ namespace bbot
return string_parser::unquote (config);
}
+ strings task_manifest::
+ unquoted_warning_regex () const
+ {
+ return string_parser::unquote (warning_regex);
+ }
+
void task_manifest::
check_config (const string& s)
{
@@ -609,6 +648,23 @@ namespace bbot
throw invalid_argument ("no variable value");
}
+ void task_manifest::
+ check_regex (const string& s)
+ {
+ try
+ {
+ regex re (string_parser::unquote (s));
+ }
+ catch (const regex_error& e)
+ {
+ // Print regex_error description if meaningful (no space).
+ //
+ ostringstream os;
+ os << "invalid regex" << e;
+ throw invalid_argument (os.str ());
+ }
+ }
+
// task_response_manifest
//
task_response_manifest::
diff --git a/libbbot/manifest.hxx b/libbbot/manifest.hxx
index b981ab4..36f2f5b 100644
--- a/libbbot/manifest.hxx
+++ b/libbbot/manifest.hxx
@@ -131,23 +131,34 @@ namespace bbot
//
strings config;
+ // Regular expressions for detecting warnings in the operation result logs
+ // (in addition to the default list of expressions).
+ // Note: could be quoted.
+ //
+ strings warning_regex;
+
strings
unquoted_config () const;
+ strings
+ unquoted_warning_regex () const;
+
task_manifest (std::string nm,
bpkg::version vr,
bpkg::repository_location rl,
strings tr,
std::string mn,
butl::optional<butl::target_triplet> tg,
- strings cf)
+ strings cf,
+ strings wr)
: name (std::move (nm)),
version (std::move (vr)),
repository (std::move (rl)),
trust (tr),
machine (std::move (mn)),
target (std::move (tg)),
- config (std::move (cf)) {}
+ config (std::move (cf)),
+ warning_regex (std::move (wr)){}
public:
task_manifest () = default; // VC export.
@@ -165,6 +176,12 @@ namespace bbot
//
static void
check_config (const std::string&);
+
+ // Check that a string is a valid (ECMAScript) regular expression. Throw
+ // invalid_argument if that's not the case.
+ //
+ static void
+ check_regex (const std::string&);
};
class LIBBBOT_EXPORT task_response_manifest
diff --git a/tests/manifest/task.test b/tests/manifest/task.test
index f2afd9c..95bb749 100644
--- a/tests/manifest/task.test
+++ b/tests/manifest/task.test
@@ -21,6 +21,7 @@ test.options += -t
machine: windows_10-msvc_14
target: x86_64-microsoft-win32-msvc14.0
config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ warning-regex: '^warning: ' '^.+: warning: '
EOF
: no-target
@@ -32,9 +33,10 @@ test.options += -t
repository: http://pkg.example.org/1/math
machine: windows_10-msvc_14
config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ warning-regex: '^warning: ' '^.+: warning: '
EOF
- : no-config
+ : no-config-no-regex
:
$* <<EOF >>EOF
: 1
@@ -135,6 +137,14 @@ test.options += -t
config: config.cc.coptions=/Z7
config: config.cc.loptions=/DEBUG
EOI
+
+ : warning-regex
+ :
+ $* <<EOI 2>'stdin:3:1: error: task warning regex redefinition' == 1
+ : 1
+ warning-regex: '^warning: '
+ warning-regex: '^.+: warning: '
+ EOI
}
: invalid
@@ -237,6 +247,26 @@ test.options += -t
}
}
+ : warning-regex
+ :
+ {
+ : empty
+ :
+ $* <<EOI 2>'stdin:2:15: error: empty task warning regex' == 1
+ : 1
+ warning-regex:
+ EOI
+
+ : regex-error
+ :
+ $* <<EOI 2>>~/EOE/ == 1
+ : 1
+ warning-regex: '^[warning: '
+ EOI
+ /stdin:2:16: error: invalid task warning regex: invalid regex.*/
+ EOE
+ }
+
: trust
:
$* <<EOI 2>'stdin:2:8: error: invalid repository certificate fingerprint' == 1