llvm.org GIT mirror llvm / 0c5f92e
Added support for reading configuration files Configuration file is read as a response file in which file names in the nested constructs `@file` are resolved relative to the directory where the including file resides. Lines in which the first non-whitespace character is '#' are considered as comments and are skipped. Trailing backslashes are used to concatenate lines in the same way as they are used in shell scripts. Differential Revision: https://reviews.llvm.org/D24926 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@321580 91177308-0d34-0410-b5e6-96231b3b80d8 Serge Pavlov 2 years ago
3 changed file(s) with 207 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
18611861 SmallVectorImpl &NewArgv,
18621862 bool MarkEOLs);
18631863
1864 /// Tokenizes content of configuration file.
1865 ///
1866 /// \param [in] Source The string representing content of config file.
1867 /// \param [in] Saver Delegates back to the caller for saving parsed strings.
1868 /// \param [out] NewArgv All parsed strings are appended to NewArgv.
1869 /// \param [in] MarkEOLs Added for compatibility with TokenizerCallback.
1870 ///
1871 /// It works like TokenizeGNUCommandLine with ability to skip comment lines.
1872 ///
1873 void tokenizeConfigFile(StringRef Source, StringSaver &Saver,
1874 SmallVectorImpl &NewArgv,
1875 bool MarkEOLs = false);
1876
1877 /// Reads command line options from the given configuration file.
1878 ///
1879 /// \param [in] CfgFileName Path to configuration file.
1880 /// \param [in] Saver Objects that saves allocated strings.
1881 /// \param [out] Argv Array to which the read options are added.
1882 /// \return true if the file was successfully read.
1883 ///
1884 /// It reads content of the specified file, tokenizes it and expands "@file"
1885 /// commands resolving file names in them relative to the directory where
1886 /// CfgFilename resides.
1887 ///
1888 bool readConfigFile(StringRef CfgFileName, StringSaver &Saver,
1889 SmallVectorImpl &Argv);
1890
18641891 /// \brief Expand response files on a command line recursively using the given
18651892 /// StringSaver and tokenization strategy. Argv should contain the command line
18661893 /// before expansion and will be modified in place. If requested, Argv will
872872 NewArgv.push_back(nullptr);
873873 }
874874
875 void cl::tokenizeConfigFile(StringRef Source, StringSaver &Saver,
876 SmallVectorImpl &NewArgv,
877 bool MarkEOLs) {
878 for (const char *Cur = Source.begin(); Cur != Source.end();) {
879 SmallString<128> Line;
880 // Check for comment line.
881 if (isWhitespace(*Cur)) {
882 while (Cur != Source.end() && isWhitespace(*Cur))
883 ++Cur;
884 continue;
885 }
886 if (*Cur == '#') {
887 while (Cur != Source.end() && *Cur != '\n')
888 ++Cur;
889 continue;
890 }
891 // Find end of the current line.
892 const char *Start = Cur;
893 for (const char *End = Source.end(); Cur != End; ++Cur) {
894 if (*Cur == '\\') {
895 if (Cur + 1 != End) {
896 ++Cur;
897 if (*Cur == '\n' ||
898 (*Cur == '\r' && (Cur + 1 != End) && Cur[1] == '\n')) {
899 Line.append(Start, Cur - 1);
900 Cur += (*Cur == '\r' ? 2 : 1);
901 Start = Cur;
902 }
903 }
904 } else if (*Cur == '\n')
905 break;
906 }
907 // Tokenize line.
908 Line.append(Start, Cur);
909 cl::TokenizeGNUCommandLine(Line, Saver, NewArgv, MarkEOLs);
910 }
911 }
912
875913 // It is called byte order marker but the UTF-8 BOM is actually not affected
876914 // by the host system's endianness.
877915 static bool hasUTF8ByteOrderMark(ArrayRef S) {
9741012 Argv.insert(Argv.begin() + I, ExpandedArgv.begin(), ExpandedArgv.end());
9751013 }
9761014 return AllExpanded;
1015 }
1016
1017 bool cl::readConfigFile(StringRef CfgFile, StringSaver &Saver,
1018 SmallVectorImpl &Argv) {
1019 if (!ExpandResponseFile(CfgFile, Saver, cl::tokenizeConfigFile, Argv,
1020 /*MarkEOLs*/ false, /*RelativeNames*/ true))
1021 return false;
1022 return ExpandResponseFiles(Saver, cl::tokenizeConfigFile, Argv,
1023 /*MarkEOLs*/ false, /*RelativeNames*/ true);
9771024 }
9781025
9791026 /// ParseEnvironmentOptions - An alternative entry point to the
203203 const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",
204204 "lmn", "o", "pqr", "st \"u", "\\v" };
205205 testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,
206 array_lengthof(Output));
207 }
208
209 TEST(CommandLineTest, TokenizeConfigFile1) {
210 const char *Input = "\\";
211 const char *const Output[] = { "\\" };
212 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
213 array_lengthof(Output));
214 }
215
216 TEST(CommandLineTest, TokenizeConfigFile2) {
217 const char *Input = "\\abc";
218 const char *const Output[] = { "abc" };
219 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
220 array_lengthof(Output));
221 }
222
223 TEST(CommandLineTest, TokenizeConfigFile3) {
224 const char *Input = "abc\\";
225 const char *const Output[] = { "abc\\" };
226 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
227 array_lengthof(Output));
228 }
229
230 TEST(CommandLineTest, TokenizeConfigFile4) {
231 const char *Input = "abc\\\n123";
232 const char *const Output[] = { "abc123" };
233 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
234 array_lengthof(Output));
235 }
236
237 TEST(CommandLineTest, TokenizeConfigFile5) {
238 const char *Input = "abc\\\r\n123";
239 const char *const Output[] = { "abc123" };
240 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
241 array_lengthof(Output));
242 }
243
244 TEST(CommandLineTest, TokenizeConfigFile6) {
245 const char *Input = "abc\\\n";
246 const char *const Output[] = { "abc" };
247 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
248 array_lengthof(Output));
249 }
250
251 TEST(CommandLineTest, TokenizeConfigFile7) {
252 const char *Input = "abc\\\r\n";
253 const char *const Output[] = { "abc" };
254 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
255 array_lengthof(Output));
256 }
257
258 TEST(CommandLineTest, TokenizeConfigFile8) {
259 SmallVector Actual;
260 BumpPtrAllocator A;
261 StringSaver Saver(A);
262 cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false);
263 EXPECT_TRUE(Actual.empty());
264 }
265
266 TEST(CommandLineTest, TokenizeConfigFile9) {
267 SmallVector Actual;
268 BumpPtrAllocator A;
269 StringSaver Saver(A);
270 cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false);
271 EXPECT_TRUE(Actual.empty());
272 }
273
274 TEST(CommandLineTest, TokenizeConfigFile10) {
275 const char *Input = "\\\nabc";
276 const char *const Output[] = { "abc" };
277 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
278 array_lengthof(Output));
279 }
280
281 TEST(CommandLineTest, TokenizeConfigFile11) {
282 const char *Input = "\\\r\nabc";
283 const char *const Output[] = { "abc" };
284 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
206285 array_lengthof(Output));
207286 }
208287
647726 EXPECT_TRUE(Opt3 == 3);
648727 }
649728
729 TEST(CommandLineTest, ReadConfigFile) {
730 llvm::SmallVector Argv;
731
732 llvm::SmallString<128> TestDir;
733 std::error_code EC =
734 llvm::sys::fs::createUniqueDirectory("unittest", TestDir);
735 EXPECT_TRUE(!EC);
736
737 llvm::SmallString<128> TestCfg;
738 llvm::sys::path::append(TestCfg, TestDir, "foo");
739 std::ofstream ConfigFile(TestCfg.c_str());
740 EXPECT_TRUE(ConfigFile.is_open());
741 ConfigFile << "# Comment\n"
742 "-option_1\n"
743 "@subconfig\n"
744 "-option_3=abcd\n"
745 "-option_4=\\\n"
746 "cdef\n";
747 ConfigFile.close();
748
749 llvm::SmallString<128> TestCfg2;
750 llvm::sys::path::append(TestCfg2, TestDir, "subconfig");
751 std::ofstream ConfigFile2(TestCfg2.c_str());
752 EXPECT_TRUE(ConfigFile2.is_open());
753 ConfigFile2 << "-option_2\n"
754 "\n"
755 " # comment\n";
756 ConfigFile2.close();
757
758 // Make sure the current directory is not the directory where config files
759 // resides. In this case the code that expands response files will not find
760 // 'subconfig' unless it resolves nested inclusions relative to the including
761 // file.
762 llvm::SmallString<128> CurrDir;
763 EC = llvm::sys::fs::current_path(CurrDir);
764 EXPECT_TRUE(!EC);
765 EXPECT_TRUE(StringRef(CurrDir) != StringRef(TestDir));
766
767 llvm::BumpPtrAllocator A;
768 llvm::StringSaver Saver(A);
769 bool Result = llvm::cl::readConfigFile(TestCfg, Saver, Argv);
770
771 EXPECT_TRUE(Result);
772 EXPECT_EQ(Argv.size(), 4U);
773 EXPECT_STREQ(Argv[0], "-option_1");
774 EXPECT_STREQ(Argv[1], "-option_2");
775 EXPECT_STREQ(Argv[2], "-option_3=abcd");
776 EXPECT_STREQ(Argv[3], "-option_4=cdef");
777
778 llvm::sys::fs::remove(TestCfg2);
779 llvm::sys::fs::remove(TestCfg);
780 llvm::sys::fs::remove(TestDir);
781 }
782
650783 } // anonymous namespace