clover: Pass unquoted compiler arguments to Clang
authorVedran Miletić <vedran@miletic.net>
Wed, 28 Sep 2016 15:11:43 +0000 (17:11 +0200)
committerFrancisco Jerez <currojerez@riseup.net>
Sun, 30 Oct 2016 19:14:59 +0000 (12:14 -0700)
OpenCL apps can quote arguments they pass to the OpenCL compiler, most
commonly include paths containing spaces.

If the Clang OpenCL compiler was called via a shell, the shell would
split the arguments with respect to to quotes and then remove quotes
before passing the arguments to the compiler. Since we call Clang as a
library, we have to split the argument with respect to quotes and then
remove quotes before passing the arguments.

v2: move to tokenize(), remove throwing of CL_INVALID_COMPILER_OPTIONS

v3: simplify parsing logic, use more C++11

v4: restore error throwing, clarify a comment

Signed-off-by: Vedran Miletić <vedran@miletic.net>
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
src/gallium/state_trackers/clover/llvm/util.hpp

index 8db6f20e5a7d19fe5263af758fa2daa91751bf89..222becd614e8a7e9803f98112f128f46b50b667b 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef CLOVER_LLVM_UTIL_HPP
 #define CLOVER_LLVM_UTIL_HPP
 
+#include "core/error.hpp"
 #include "util/u_debug.h"
 
 #include <vector>
@@ -42,11 +43,42 @@ namespace clover {
       inline std::vector<std::string>
       tokenize(const std::string &s) {
          std::vector<std::string> ss;
-         std::istringstream iss(s);
-         std::string t;
+         std::ostringstream oss;
 
-         while (getline(iss, t, ' '))
-            ss.push_back(t);
+         // OpenCL programs can pass a quoted argument, most frequently the
+         // include path. This is useful so that path containing spaces is
+         // treated as a single argument instead of being split by the spaces.
+         // Additionally, the argument should also be unquoted before being
+         // passed to the compiler. We avoid using std::string::replace here to
+         // remove quotes, as the single and double quote characters can be a
+         // part of the file name.
+         bool escape_next = false;
+         bool in_quote_double = false;
+         bool in_quote_single = false;
+
+         for (auto c : s) {
+            if (escape_next) {
+               oss.put(c);
+               escape_next = false;
+            } else if (c == '\\') {
+               escape_next = true;
+            } else if (c == '"' && !in_quote_single) {
+               in_quote_double = !in_quote_double;
+            } else if (c == '\'' && !in_quote_double) {
+               in_quote_single = !in_quote_single;
+            } else if (c != ' ' || in_quote_single || in_quote_double) {
+               oss.put(c);
+            } else if (oss.tellp() > 0) {
+               ss.emplace_back(oss.str());
+               oss.str("");
+            }
+         }
+
+         if (oss.tellp() > 0)
+            ss.emplace_back(oss.str());
+
+         if (in_quote_double || in_quote_single)
+            throw invalid_build_options_error();
 
          return ss;
       }