diff --git a/engine/src/flutter/tools/licenses_cpp/data/exclude.txt b/engine/src/flutter/tools/licenses_cpp/data/exclude.txt index c208a4d13a7..f480e262d66 100644 --- a/engine/src/flutter/tools/licenses_cpp/data/exclude.txt +++ b/engine/src/flutter/tools/licenses_cpp/data/exclude.txt @@ -3,3 +3,7 @@ .*\/test\/.* .*\/testing\/.* .*\/tests\/.* +.*/third_party/swiftshader/third_party/llvm-16.0/.* +.*/third_party/swiftshader/third_party/llvm-10.0/.* +.*/third_party/llvm-project/.* +.*/third_party/ocmock/.* diff --git a/engine/src/flutter/tools/licenses_cpp/src/comments.cc b/engine/src/flutter/tools/licenses_cpp/src/comments.cc index 08c5d5d5e88..a1b198cb42d 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/comments.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/comments.cc @@ -349,9 +349,9 @@ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[28] = {0, 0, 0, 0, 0, 0, 0, 9, 7, 7, - 1, 7, 4, 4, 6, 6, 0, 1, 0, 1, - 2, 3, 0, 5, 0, 2, 5, 0}; +static const flex_int16_t yy_accept[37] = { + 0, 0, 0, 0, 0, 0, 0, 9, 7, 7, 1, 7, 8, 4, 8, 6, 6, 0, 1, + 0, 1, 2, 0, 4, 0, 0, 0, 5, 0, 2, 2, 0, 3, 0, 5, 0, 0}; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -371,23 +371,27 @@ static const YY_CHAR yy_ec[256] = { static const YY_CHAR yy_meta[7] = {0, 1, 1, 2, 1, 1, 3}; -static const flex_int16_t yy_base[36] = { - 0, 0, 0, 27, 24, 14, 12, 10, 34, 5, 0, 7, 34, 0, 34, 12, 0, 0, - 0, 0, 0, 34, 0, 0, 0, 34, 0, 34, 18, 21, 24, 27, 5, 3, 30, 0}; +static const flex_int16_t yy_base[45] = { + 0, 0, 0, 4, 7, 18, 13, 14, 62, 11, 0, 13, 17, 62, 20, + 62, 25, 0, 0, 0, 0, 29, 5, 62, 0, 32, 0, 0, 0, 62, + 0, 35, 62, 2, 0, 0, 62, 40, 43, 46, 49, 52, 55, 58, 0}; -static const flex_int16_t yy_def[36] = { - 0, 28, 28, 29, 29, 30, 30, 27, 27, 27, 31, 27, 27, 32, 27, 27, 9, 31, - 11, 31, 33, 27, 15, 34, 35, 27, 34, 0, 27, 27, 27, 27, 27, 27, 27, 27}; +static const flex_int16_t yy_def[45] = { + 0, 37, 37, 38, 38, 39, 39, 36, 36, 36, 40, 36, 41, 36, 41, + 36, 36, 9, 40, 11, 40, 36, 14, 36, 14, 42, 16, 43, 44, 36, + 21, 42, 36, 31, 43, 31, 0, 36, 36, 36, 36, 36, 36, 36, 36}; -static const flex_int16_t yy_nxt[41] = { - 0, 27, 9, 23, 10, 25, 11, 16, 21, 17, 27, 18, 20, 17, - 22, 15, 23, 15, 24, 8, 8, 8, 12, 12, 12, 14, 14, 14, - 19, 13, 19, 26, 13, 26, 7, 27, 27, 27, 27, 27, 27}; +static const flex_int16_t yy_nxt[69] = { + 0, 36, 9, 27, 10, 36, 11, 13, 35, 14, 13, 22, 14, 17, 36, 18, 16, 19, + 21, 18, 23, 16, 24, 23, 36, 24, 25, 26, 36, 27, 36, 28, 29, 36, 30, 32, + 36, 33, 32, 36, 33, 8, 8, 8, 12, 12, 12, 15, 15, 15, 20, 36, 20, 22, + 22, 22, 31, 31, 31, 34, 36, 34, 7, 36, 36, 36, 36, 36, 36}; -static const flex_int16_t yy_chk[41] = { - 0, 0, 2, 35, 2, 33, 2, 9, 32, 9, 7, 9, 11, 11, - 15, 6, 15, 5, 15, 28, 28, 28, 29, 29, 29, 30, 30, 30, - 31, 4, 31, 34, 3, 34, 27, 27, 27, 27, 27, 27, 27}; +static const flex_int16_t yy_chk[69] = { + 0, 0, 2, 44, 2, 0, 2, 3, 33, 3, 4, 22, 4, 9, 7, 9, 6, 9, + 11, 11, 12, 5, 12, 14, 0, 14, 14, 16, 0, 16, 0, 16, 21, 0, 21, 25, + 0, 25, 31, 0, 31, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 0, 40, 41, + 41, 41, 42, 42, 42, 43, 0, 43, 36, 36, 36, 36, 36, 36, 36}; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. @@ -421,9 +425,9 @@ struct LexerContext { std::function callback; std::string buffer; }; -#line 462 "comments.cc" +#line 471 "comments.cc" -#line 464 "comments.cc" +#line 473 "comments.cc" #define INITIAL 0 #define C_COMMENT 1 @@ -681,7 +685,7 @@ YY_DECL { { #line 40 "comments.l" -#line 729 "comments.cc" +#line 738 "comments.cc" while (/*CONSTCOND*/ 1) /* loops until end-of-file is reached */ { @@ -706,12 +710,12 @@ YY_DECL { } while (yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state) { yy_current_state = (int)yy_def[yy_current_state]; - if (yy_current_state >= 28) + if (yy_current_state >= 37) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; - } while (yy_base[yy_current_state] != 34); + } while (yy_base[yy_current_state] != 62); yy_find_action: yy_act = yy_accept[yy_current_state]; @@ -751,10 +755,12 @@ YY_DECL { YY_BREAK case 3: + /* rule 3 can match eol */ YY_RULE_SETUP #line 50 "comments.l" { BEGIN(INITIAL); + CommentsUtil::AddCEndTrimLine(&yyextra->buffer, yytext, yyleng - 2); yyextra->callback(yyextra->buffer); yyextra->buffer.clear(); } @@ -762,16 +768,16 @@ YY_DECL { case 4: /* rule 4 can match eol */ YY_RULE_SETUP -#line 55 "comments.l" +#line 56 "comments.l" { - yyextra->buffer.append(yytext, yyleng); + CommentsUtil::AddCTrimLine(&yyextra->buffer, yytext, yyleng); } YY_BREAK case 5: /* rule 5 can match eol */ YY_RULE_SETUP -#line 61 "comments.l" +#line 62 "comments.l" { yyextra->buffer.append("\n", 1); CommentsUtil::AddTrimLine(&yyextra->buffer, yytext + 1, yyleng - 1); @@ -780,7 +786,7 @@ YY_DECL { case 6: /* rule 6 can match eol */ YY_RULE_SETUP -#line 65 "comments.l" +#line 66 "comments.l" { BEGIN(INITIAL); yyextra->callback(yyextra->buffer); @@ -791,16 +797,16 @@ YY_DECL { case 7: /* rule 7 can match eol */ YY_RULE_SETUP -#line 72 "comments.l" +#line 73 "comments.l" { } YY_BREAK case 8: YY_RULE_SETUP -#line 73 "comments.l" +#line 74 "comments.l" ECHO; YY_BREAK -#line 854 "comments.cc" +#line 865 "comments.cc" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(C_COMMENT): case YY_STATE_EOF(BLOCK): @@ -1080,7 +1086,7 @@ static yy_state_type yy_get_previous_state(yyscan_t yyscanner) { } while (yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state) { yy_current_state = (int)yy_def[yy_current_state]; - if (yy_current_state >= 28) + if (yy_current_state >= 37) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -1108,11 +1114,11 @@ static yy_state_type yy_try_NUL_trans(yy_state_type yy_current_state, } while (yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state) { yy_current_state = (int)yy_def[yy_current_state]; - if (yy_current_state >= 28) + if (yy_current_state >= 37) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 27); + yy_is_jam = (yy_current_state == 36); (void)yyg; return yy_is_jam ? 0 : yy_current_state; @@ -1909,7 +1915,7 @@ void yyfree(void* ptr, yyscan_t yyscanner) { #define YYTABLES_NAME "yytables" -#line 73 "comments.l" +#line 74 "comments.l" void IterateComments(const char* buffer, size_t size, diff --git a/engine/src/flutter/tools/licenses_cpp/src/comments.l b/engine/src/flutter/tools/licenses_cpp/src/comments.l index 2fdaedfa500..a3e963de956 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/comments.l +++ b/engine/src/flutter/tools/licenses_cpp/src/comments.l @@ -42,18 +42,19 @@ COMMENT_START (\/\/|#) BEGIN(BLOCK); CommentsUtil::AddTrimLine(&yyextra->buffer, yytext, yyleng); } -^[ \t]*\/\*[\n]? { +^[ \t]*\/\*[\*]*[\n]? { BEGIN(C_COMMENT); } { - \*\/ { + .*\*\/.*\n { BEGIN(INITIAL); + CommentsUtil::AddCEndTrimLine(&yyextra->buffer, yytext, yyleng - 2); yyextra->callback(yyextra->buffer); yyextra->buffer.clear(); } - \n|. { - yyextra->buffer.append(yytext, yyleng); + .*\n { + CommentsUtil::AddCTrimLine(&yyextra->buffer, yytext, yyleng); } } diff --git a/engine/src/flutter/tools/licenses_cpp/src/comments_unittests.cc b/engine/src/flutter/tools/licenses_cpp/src/comments_unittests.cc index 635f024ff55..7405f0b50c0 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/comments_unittests.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/comments_unittests.cc @@ -66,3 +66,35 @@ daa ASSERT_EQ(comments.size(), 1u); EXPECT_EQ(comments[0], "hello\nworld"); } + +TEST(CommentsTest, CWithLeadingStars) { + std::string test = R"test( +/************* + * hello + * world + */ +)test"; + + std::vector comments; + IterateComments(test.c_str(), test.size(), [&](std::string_view comment) { + comments.push_back(std::string(comment)); + }); + + ASSERT_EQ(comments.size(), 1u); + EXPECT_EQ(comments[0], "hello\nworld\n"); +} + +TEST(CommentsTest, CTextOnEndingLine) { + std::string test = R"test( +/*hello +world*/ +)test"; + + std::vector comments; + IterateComments(test.c_str(), test.size(), [&](std::string_view comment) { + comments.push_back(std::string(comment)); + }); + + ASSERT_EQ(comments.size(), 1u); + EXPECT_EQ(comments[0], "hello\nworld"); +} diff --git a/engine/src/flutter/tools/licenses_cpp/src/comments_util.cc b/engine/src/flutter/tools/licenses_cpp/src/comments_util.cc index 6daa108e6c7..dcfb6c45a2e 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/comments_util.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/comments_util.cc @@ -13,3 +13,21 @@ void CommentsUtil::AddTrimLine(std::string* buffer, RE2::FullMatch(re2::StringPiece(text), regex, &captured_content); buffer->append(captured_content); } + +void CommentsUtil::AddCTrimLine(std::string* buffer, + const char* text, + size_t length) { + std::string chopped(text, length); + RE2 regex(R"regex(^\s*\**\s?)regex"); + RE2::Replace(&chopped, regex, ""); + buffer->append(chopped); +} + +void CommentsUtil::AddCEndTrimLine(std::string* buffer, + const char* text, + size_t length) { + RE2 regex(R"regex(^\s*(.*?)\*/)regex"); + re2::StringPiece captured_content; + RE2::PartialMatch(re2::StringPiece(text), regex, &captured_content); + buffer->append(captured_content); +} diff --git a/engine/src/flutter/tools/licenses_cpp/src/comments_util.h b/engine/src/flutter/tools/licenses_cpp/src/comments_util.h index d5be49e787b..ac56cb09a70 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/comments_util.h +++ b/engine/src/flutter/tools/licenses_cpp/src/comments_util.h @@ -11,6 +11,12 @@ class CommentsUtil { public: static void AddTrimLine(std::string* buffer, const char* text, size_t length); + static void AddCTrimLine(std::string* buffer, + const char* text, + size_t length); + static void AddCEndTrimLine(std::string* buffer, + const char* text, + size_t length); }; #endif // FLUTTER_TOOLS_LICENSES_CPP_SRC_COMMENTS_UTIL_H_ diff --git a/engine/src/flutter/tools/licenses_cpp/src/license_checker.cc b/engine/src/flutter/tools/licenses_cpp/src/license_checker.cc index c5ee1a29348..5ba6ea59e93 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/license_checker.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/license_checker.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -67,15 +68,14 @@ absl::StatusOr> GitLsFiles(const fs::path& repo_path) { return files; } -absl::Status CheckLicense(const fs::path& git_repo) { +std::optional FindLicense(const fs::path& path) { for (std::string_view license_name : kLicenseFileNames) { - fs::path license_path = git_repo / license_name; + fs::path license_path = path / license_name; if (fs::exists(license_path)) { - return absl::UnimplementedError(""); + return license_path; } } - return absl::NotFoundError( - absl::StrCat("Expected LICENSE at ", git_repo.string())); + return std::nullopt; } void PrintProgress(size_t idx, size_t count) { @@ -124,20 +124,76 @@ class LicensesWriter { bool first_write_ = true; }; -std::string GetPackageName(const fs::path& full_path) { - std::optional result; +struct Package { + std::string name; + std::optional license_file; +}; + +Package GetPackage(const fs::path& working_dir, const fs::path& full_path) { + Package result = { + .name = "engine", + .license_file = FindLicense(working_dir), + }; + fs::path relative = fs::relative(full_path, working_dir); bool after_third_party = false; - for (const fs::path& component : full_path) { + fs::path current = working_dir; + for (const fs::path& component : relative) { + current /= component; if (after_third_party) { - result = component; + result.name = component; after_third_party = false; + result.license_file = FindLicense(current); } else if (component.string() == "third_party") { after_third_party = true; + result.license_file = std::nullopt; } } - return result.has_value() ? result.value() : "engine"; + return result; } + +std::string ReadFile(const fs::path& path) { + std::ifstream stream(path); + assert(stream.good()); + std::string license((std::istreambuf_iterator(stream)), + std::istreambuf_iterator()); + if (license[license.size() - 1] == '\n') { + license.pop_back(); + } + return license; +} + +class LicenseMap { + public: + void Add(std::string_view package, std::string_view license) { + auto package_emplace_result = + map_.try_emplace(license, absl::flat_hash_set()); + absl::flat_hash_set& comment_set = + package_emplace_result.first->second; + if (comment_set.find(package) == comment_set.end()) { + // License is already seen. + comment_set.emplace(std::string(package)); + } + } + + void AddFile(std::string_view package, const fs::path& path) { + if (!license_files_.contains(path.string())) { + Add(package, ReadFile(path)); + license_files_.insert(path); + } + } + + void Write(std::ostream& licenses) { + LicensesWriter writer(licenses); + for (const auto& comment_entry : map_) { + writer.Write(comment_entry.second, comment_entry.first); + } + } + + private: + absl::btree_map> map_; + absl::flat_hash_set license_files_; +}; } // namespace std::vector LicenseChecker::Run(std::string_view working_dir, @@ -145,27 +201,22 @@ std::vector LicenseChecker::Run(std::string_view working_dir, const Data& data) { std::vector errors; std::vector git_repos = GetGitRepos(working_dir); + fs::path working_dir_path(working_dir); RE2 pattern(kHeaderLicenseRegex); size_t count = 0; + LicenseMap license_map; for (const fs::path& git_repo : git_repos) { if (IsStdoutTerminal()) { PrintProgress(count++, git_repos.size()); } - absl::Status license_status = CheckLicense(git_repo); - if (!license_status.ok() && - license_status.code() != absl::StatusCode::kUnimplemented) { - errors.push_back(license_status); - } absl::StatusOr> git_files = GitLsFiles(git_repo); if (!git_files.ok()) { errors.push_back(git_files.status()); return errors; } - LicensesWriter writer(licenses); - absl::btree_map> license_map; for (const std::string& git_file : git_files.value()) { bool did_find_copyright = false; fs::path full_path = git_repo / git_file; @@ -175,7 +226,10 @@ std::vector LicenseChecker::Run(std::string_view working_dir, continue; } - std::string package = GetPackageName(full_path); + Package package = GetPackage(working_dir_path, full_path); + if (package.license_file.has_value()) { + license_map.AddFile(package.name, package.license_file.value()); + } VLOG(1) << full_path.string(); absl::StatusOr file = MMapFile::Make(full_path.string()); @@ -196,27 +250,18 @@ std::vector LicenseChecker::Run(std::string_view working_dir, if (RE2::PartialMatch(comment, pattern, &match)) { did_find_copyright = true; VLOG(1) << comment; - - auto package_emplace_result = license_map.try_emplace( - comment, absl::flat_hash_set()); - absl::flat_hash_set& comment_set = - package_emplace_result.first->second; - if (comment_set.find(package) != comment_set.end()) { - // License is already seen. - return; + if (!package.license_file.has_value()) { + license_map.Add(package.name, comment); } - comment_set.emplace(std::string(package)); } }); - if (!did_find_copyright) { + if (!did_find_copyright && !package.license_file.has_value()) { errors.push_back( absl::NotFoundError("Expected copyright in " + full_path.string())); } } - for (const auto& comment_entry : license_map) { - writer.Write(comment_entry.second, comment_entry.first); - } } + license_map.Write(licenses); if (IsStdoutTerminal()) { PrintProgress(count++, git_repos.size()); std::cout << std::endl; diff --git a/engine/src/flutter/tools/licenses_cpp/src/license_checker_unittests.cc b/engine/src/flutter/tools/licenses_cpp/src/license_checker_unittests.cc index a64734a3086..d4957cfc11f 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/license_checker_unittests.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/license_checker_unittests.cc @@ -65,8 +65,7 @@ void main() { } )header"; -const char* kLicense = R"lic( -Test License +const char* kLicense = R"lic(Test License v2.0 )lic"; @@ -109,6 +108,30 @@ bool FindError(const std::vector& errors, RE2::PartialMatch(status.message(), regex); }) != errors.end(); } + +class Repo { + public: + void Add(const std::string& file) { files_.emplace_back(std::string(file)); } + + absl::Status Commit() { + if (std::system("git init") != 0) { + return absl::InternalError("git init failed"); + } + for (const std::string& file : files_) { + if (std::system(("git add " + file).c_str()) != 0) { + return absl::InternalError("git add failed: " + file); + } + } + if (std::system("git commit -m \"test\"") != 0) { + return absl::InternalError("git commit failed"); + } + return absl::OkStatus(); + } + + private: + std::vector files_; +}; + } // namespace TEST_F(LicenseCheckerTest, SimplePass) { @@ -119,12 +142,12 @@ TEST_F(LicenseCheckerTest, SimplePass) { ASSERT_TRUE(data.ok()); fs::current_path(*temp_path); - ASSERT_EQ(std::system("git init"), 0); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "main.cc").ok()); ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); - ASSERT_EQ(std::system("git add main.cc"), 0); - ASSERT_EQ(std::system("git add LICENSE"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add(*temp_path / "main.cc"); + repo.Add(*temp_path / "LICENSE"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = @@ -132,7 +155,7 @@ TEST_F(LicenseCheckerTest, SimplePass) { EXPECT_EQ(errors.size(), 0u); } -TEST_F(LicenseCheckerTest, SimpleMissingLicense) { +TEST_F(LicenseCheckerTest, SimpleMissingFileLicense) { absl::StatusOr temp_path = MakeTempDir(); ASSERT_TRUE(temp_path.ok()); @@ -140,22 +163,20 @@ TEST_F(LicenseCheckerTest, SimpleMissingLicense) { ASSERT_TRUE(data.ok()); fs::current_path(*temp_path); - ASSERT_EQ(std::system("git init"), 0); ASSERT_EQ(std::system("echo \"Hello world!\" > main.cc"), 0); - ASSERT_EQ(std::system("git add main.cc"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add(*temp_path / "main.cc"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = LicenseChecker::Run(temp_path->string(), ss, *data); - EXPECT_EQ(errors.size(), 2u); - EXPECT_TRUE( - FindError(errors, absl::StatusCode::kNotFound, "Expected LICENSE at")); + EXPECT_EQ(errors.size(), 1u); EXPECT_TRUE(FindError(errors, absl::StatusCode::kNotFound, "Expected copyright in.*main.cc")); } -TEST_F(LicenseCheckerTest, SimpleWritesLicensesFile) { +TEST_F(LicenseCheckerTest, SimpleWritesFileLicensesFile) { absl::StatusOr temp_path = MakeTempDir(); ASSERT_TRUE(temp_path.ok()); @@ -163,12 +184,10 @@ TEST_F(LicenseCheckerTest, SimpleWritesLicensesFile) { ASSERT_TRUE(data.ok()); fs::current_path(*temp_path); - ASSERT_EQ(std::system("git init"), 0); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "main.cc").ok()); - ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); - ASSERT_EQ(std::system("git add main.cc"), 0); - ASSERT_EQ(std::system("git add LICENSE"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add(*temp_path / "main.cc"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = @@ -181,7 +200,7 @@ Copyright Test )output"); } -TEST_F(LicenseCheckerTest, SimpleWritesTwoLicensesFiles) { +TEST_F(LicenseCheckerTest, SimpleWritesTwoFileLicensesFiles) { absl::StatusOr temp_path = MakeTempDir(); ASSERT_TRUE(temp_path.ok()); @@ -189,14 +208,12 @@ TEST_F(LicenseCheckerTest, SimpleWritesTwoLicensesFiles) { ASSERT_TRUE(data.ok()); fs::current_path(*temp_path); - ASSERT_EQ(std::system("git init"), 0); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "main.cc").ok()); ASSERT_TRUE(WriteFile(kCHeader, *temp_path / "cmain.cc").ok()); - ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); - ASSERT_EQ(std::system("git add main.cc"), 0); - ASSERT_EQ(std::system("git add cmain.cc"), 0); - ASSERT_EQ(std::system("git add LICENSE"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add("main.cc"); + repo.Add("cmain.cc"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = @@ -214,7 +231,7 @@ Copyright Test )output"); } -TEST_F(LicenseCheckerTest, SimpleWritesDuplicateLicensesFiles) { +TEST_F(LicenseCheckerTest, SimpleWritesDuplicateFileLicensesFiles) { absl::StatusOr temp_path = MakeTempDir(); ASSERT_TRUE(temp_path.ok()); @@ -222,14 +239,12 @@ TEST_F(LicenseCheckerTest, SimpleWritesDuplicateLicensesFiles) { ASSERT_TRUE(data.ok()); fs::current_path(*temp_path); - ASSERT_EQ(std::system("git init"), 0); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "a.cc").ok()); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "b.cc").ok()); - ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); - ASSERT_EQ(std::system("git add a.cc"), 0); - ASSERT_EQ(std::system("git add b.cc"), 0); - ASSERT_EQ(std::system("git add LICENSE"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add("a.cc"); + repo.Add("b.cc"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = @@ -242,7 +257,7 @@ Copyright Test )output"); } -TEST_F(LicenseCheckerTest, MultiplePackages) { +TEST_F(LicenseCheckerTest, FileLicenseMultiplePackages) { absl::StatusOr temp_path = MakeTempDir(); ASSERT_TRUE(temp_path.ok()); @@ -251,19 +266,13 @@ TEST_F(LicenseCheckerTest, MultiplePackages) { fs::current_path(*temp_path); ASSERT_EQ(std::system("mkdir -p third_party/foobar"), 0); - ASSERT_EQ(std::system("git init"), 0); ASSERT_TRUE(WriteFile(kHeader, *temp_path / "a.cc").ok()); ASSERT_TRUE( WriteFile(kHeader, *temp_path / "third_party" / "foobar" / "b.cc").ok()); - ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); - ASSERT_TRUE( - WriteFile(kLicense, *temp_path / "third_party" / "foobar" / "LICENSE") - .ok()); - ASSERT_EQ(std::system("git add a.cc"), 0); - ASSERT_EQ(std::system("git add third_party/foobar/b.cc"), 0); - ASSERT_EQ(std::system("git add third_party/foobar/LICENSE"), 0); - ASSERT_EQ(std::system("git add LICENSE"), 0); - ASSERT_EQ(std::system("git commit -m \"test\""), 0); + Repo repo; + repo.Add("a.cc"); + repo.Add("third_party/foobar/b.cc"); + ASSERT_TRUE(repo.Commit().ok()); std::stringstream ss; std::vector errors = @@ -276,3 +285,66 @@ foobar Copyright Test )output"); } + +TEST_F(LicenseCheckerTest, SimpleDirectoryLicense) { + absl::StatusOr temp_path = MakeTempDir(); + ASSERT_TRUE(temp_path.ok()); + + absl::StatusOr data = MakeTestData(); + ASSERT_TRUE(data.ok()); + + fs::current_path(*temp_path); + ASSERT_EQ(std::system("echo \"Hello world!\" > main.cc"), 0); + ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); + Repo repo; + repo.Add("main.cc"); + repo.Add("LICENSE"); + ASSERT_TRUE(repo.Commit().ok()); + + std::stringstream ss; + std::vector errors = + LicenseChecker::Run(temp_path->string(), ss, *data); + EXPECT_EQ(errors.size(), 0u); + + EXPECT_EQ(ss.str(), R"output(engine + +Test License +v2.0 +)output"); +} + +TEST_F(LicenseCheckerTest, ThirdyPartyDirectoryLicense) { + absl::StatusOr temp_path = MakeTempDir(); + ASSERT_TRUE(temp_path.ok()); + + absl::StatusOr data = MakeTestData(); + ASSERT_TRUE(data.ok()); + + fs::current_path(*temp_path); + ASSERT_EQ(std::system("mkdir -p third_party/foobar"), 0); + ASSERT_EQ(std::system("echo \"Hello world!\" > main.cc"), 0); + ASSERT_EQ(std::system("echo \"Hello world!\" > third_party/foobar/foo.cc"), + 0); + ASSERT_TRUE(WriteFile(kLicense, *temp_path / "LICENSE").ok()); + ASSERT_TRUE( + WriteFile(kLicense, *temp_path / "third_party" / "foobar" / "LICENSE") + .ok()); + Repo repo; + repo.Add("main.cc"); + repo.Add("LICENSE"); + repo.Add("third_party/foobar/foo.cc"); + repo.Add("third_party/foobar/LICENSE"); + ASSERT_TRUE(repo.Commit().ok()); + + std::stringstream ss; + std::vector errors = + LicenseChecker::Run(temp_path->string(), ss, *data); + EXPECT_EQ(errors.size(), 0u); + + EXPECT_EQ(ss.str(), R"output(engine +foobar + +Test License +v2.0 +)output"); +} diff --git a/engine/src/flutter/tools/licenses_cpp/src/main.cc b/engine/src/flutter/tools/licenses_cpp/src/main.cc index a9661be75d9..e484e58300f 100644 --- a/engine/src/flutter/tools/licenses_cpp/src/main.cc +++ b/engine/src/flutter/tools/licenses_cpp/src/main.cc @@ -34,7 +34,7 @@ int main(int argc, char** argv) { absl::SetGlobalVLogLevel(absl::GetFlag(FLAGS_v)); std::optional working_dir = absl::GetFlag(FLAGS_working_dir); - std::optional data_dir = absl::GetFlag(FLAGS_working_dir); + std::optional data_dir = absl::GetFlag(FLAGS_data_dir); std::optional licenses_path = absl::GetFlag(FLAGS_licenses_path); if (working_dir.has_value() && data_dir.has_value() && licenses_path.has_value()) { @@ -51,6 +51,10 @@ int main(int argc, char** argv) { std::cerr << "Expected --working_dir flag." << std::endl; } + if (!licenses_path.has_value()) { + std::cerr << "Expected --licenses_path flag." << std::endl; + } + if (!data_dir.has_value()) { std::cerr << "Expected --data_dir flag." << std::endl; }