| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <string.h> | |
| #include "gn/input_file.h" | |
| #include "gn/parse_tree.h" | |
| #include "gn/source_file.h" | |
| #include "base/logging.h" | |
| #include "gn/filesystem_utils.h" | |
| #include "gn/source_dir.h" | |
| #include "util/build_config.h" | |
| namespace { | |
| void AssertValueSourceFileString(const std::string& s) { | |
| #if defined(OS_WIN) | |
| DCHECK(s[0] == '/' || | |
| (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2]))); | |
| #else | |
| DCHECK(s[0] == '/'); | |
| #endif | |
| DCHECK(!EndsWithSlash(s)) << s; | |
| } | |
| bool EndsWithExtension(std::string_view str, std::string_view ext) { | |
| return str.size() > ext.size() && str[str.size() - ext.size() - 1] == '.' && | |
| !::memcmp(str.data() + str.size() - ext.size(), ext.data(), | |
| ext.size()); | |
| } | |
| SourceFile::Type GetSourceFileType(const std::string& file) { | |
| size_t size = file.size(); | |
| const char* str = file.data(); | |
| // First, single-char extensions. | |
| if (size > 2 && str[size - 2] == '.') { | |
| switch (str[size - 1]) { | |
| case 'c': | |
| return SourceFile::SOURCE_C; // .c | |
| case 'h': | |
| return SourceFile::SOURCE_H; // .h | |
| case 'm': | |
| return SourceFile::SOURCE_M; // .m | |
| case 'o': | |
| return SourceFile::SOURCE_O; // .o | |
| case 'S': | |
| case 's': | |
| return SourceFile::SOURCE_S; // .S and .s | |
| default: | |
| return SourceFile::SOURCE_UNKNOWN; | |
| } | |
| } | |
| // Second, two-char extensions | |
| if (size > 3 && str[size - 3] == '.') { | |
| #define TAG2(c1, c2) ((unsigned)(c1) | ((unsigned)(c2) << 8)) | |
| switch (TAG2(str[size - 2], str[size - 1])) { | |
| case TAG2('c', 'c'): | |
| return SourceFile::SOURCE_CPP; // .cc | |
| case TAG2('g', 'o'): | |
| return SourceFile::SOURCE_GO; // .go | |
| case TAG2('h', 'h'): | |
| return SourceFile::SOURCE_H; // .hh | |
| case TAG2('m', 'm'): | |
| return SourceFile::SOURCE_MM; // .mm | |
| case TAG2('r', 'c'): | |
| return SourceFile::SOURCE_RC; // .rc | |
| case TAG2('r', 's'): | |
| return SourceFile::SOURCE_RS; // .rs | |
| default: | |
| return SourceFile::SOURCE_UNKNOWN; | |
| } | |
| #undef TAG2 | |
| } | |
| if (size > 4 && str[size - 4] == '.') { | |
| #define TAG3(c1, c2, c3) \ | |
| ((unsigned)(c1) | ((unsigned)(c2) << 8) | ((unsigned)(c3) << 16)) | |
| switch (TAG3(str[size - 3], str[size - 2], str[size - 1])) { | |
| case TAG3('c', 'p', 'p'): | |
| case TAG3('c', 'x', 'x'): | |
| case TAG3('c', '+', '+'): | |
| return SourceFile::SOURCE_CPP; | |
| case TAG3('h', 'p', 'p'): | |
| case TAG3('h', 'x', 'x'): | |
| case TAG3('i', 'n', 'c'): | |
| case TAG3('i', 'p', 'p'): | |
| case TAG3('i', 'n', 'l'): | |
| return SourceFile::SOURCE_H; | |
| case TAG3('a', 's', 'm'): | |
| return SourceFile::SOURCE_S; | |
| case TAG3('d', 'e', 'f'): | |
| return SourceFile::SOURCE_DEF; | |
| case TAG3('o', 'b', 'j'): | |
| return SourceFile::SOURCE_O; | |
| default: | |
| return SourceFile::SOURCE_UNKNOWN; | |
| } | |
| #undef TAG3 | |
| } | |
| // Other cases | |
| if (EndsWithExtension(file, "hpp11")) | |
| return SourceFile::SOURCE_H; | |
| if (EndsWithExtension(file, "swift")) | |
| return SourceFile::SOURCE_SWIFT; | |
| if (EndsWithExtension(file, "swiftmodule")) | |
| return SourceFile::SOURCE_SWIFTMODULE; | |
| if (EndsWithExtension(file, "modulemap")) | |
| return SourceFile::SOURCE_MODULEMAP; | |
| return SourceFile::SOURCE_UNKNOWN; | |
| } | |
| std::string Normalized(std::string value) { | |
| DCHECK(!value.empty()); | |
| AssertValueSourceFileString(value); | |
| NormalizePath(&value); | |
| return value; | |
| } | |
| } // namespace | |
| SourceFile::SourceFile(const std::string& value) | |
| : SourceFile(StringAtom(Normalized(value))) {} | |
| SourceFile::SourceFile(std::string&& value) | |
| : SourceFile(StringAtom(Normalized(std::move(value)))) {} | |
| SourceFile::SourceFile(StringAtom value) : value_(value) {} | |
| SourceFile::Type SourceFile::GetType() const { | |
| return GetSourceFileType(value_.str()); | |
| } | |
| bool SourceFile::IsDefType() const { | |
| std::string_view v = value_.str(); | |
| return EndsWithExtension(v, "def"); | |
| } | |
| bool SourceFile::IsObjectType() const { | |
| std::string_view v = value_.str(); | |
| return EndsWithExtension(v, "o") || EndsWithExtension(v, "obj"); | |
| } | |
| bool SourceFile::IsModuleMapType() const { | |
| std::string_view v = value_.str(); | |
| return EndsWithExtension(v, "modulemap"); | |
| } | |
| bool SourceFile::IsSwiftType() const { | |
| std::string_view v = value_.str(); | |
| return EndsWithExtension(v, "swift"); | |
| } | |
| bool SourceFile::IsSwiftModuleType() const { | |
| std::string_view v = value_.str(); | |
| return EndsWithExtension(v, "swiftmodule"); | |
| } | |
| std::string SourceFile::GetName() const { | |
| if (is_null()) | |
| return std::string(); | |
| const std::string& value = value_.str(); | |
| DCHECK(value.find('/') != std::string::npos); | |
| size_t last_slash = value.rfind('/'); | |
| return std::string(&value[last_slash + 1], value.size() - last_slash - 1); | |
| } | |
| SourceDir SourceFile::GetDir() const { | |
| if (is_null()) | |
| return SourceDir(); | |
| const std::string& value = value_.str(); | |
| DCHECK(value.find('/') != std::string::npos); | |
| size_t last_slash = value.rfind('/'); | |
| return SourceDir(value.substr(0, last_slash + 1)); | |
| } | |
| base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const { | |
| return ResolvePath(value_.str(), true, source_root); | |
| } | |
| void SourceFile::SetValue(const std::string& value) { | |
| value_ = StringAtom(value); | |
| } | |
| SourceFileTypeSet::SourceFileTypeSet() : empty_(true) { | |
| memset(flags_, 0, | |
| sizeof(bool) * static_cast<int>(SourceFile::SOURCE_NUMTYPES)); | |
| } | |
| bool SourceFileTypeSet::CSourceUsed() const { | |
| return empty_ || Get(SourceFile::SOURCE_CPP) || | |
| Get(SourceFile::SOURCE_MODULEMAP) || Get(SourceFile::SOURCE_H) || | |
| Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) || | |
| Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) || | |
| Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) || | |
| Get(SourceFile::SOURCE_DEF); | |
| } | |
| bool SourceFileTypeSet::RustSourceUsed() const { | |
| return Get(SourceFile::SOURCE_RS); | |
| } | |
| bool SourceFileTypeSet::GoSourceUsed() const { | |
| return Get(SourceFile::SOURCE_GO); | |
| } | |
| bool SourceFileTypeSet::SwiftSourceUsed() const { | |
| return Get(SourceFile::SOURCE_SWIFT); | |
| } | |
| bool SourceFileTypeSet::MixedSourceUsed() const { | |
| return (1 << static_cast<int>(CSourceUsed()) | |
| << static_cast<int>(RustSourceUsed()) | |
| << static_cast<int>(GoSourceUsed()) | |
| << static_cast<int>(SwiftSourceUsed())) > 2; | |
| } | |
| bool InSourceAllowList(const ParseNode* node, const SourceFileSet* allowlist) { | |
| if (!allowlist) | |
| return true; | |
| LocationRange range = node->GetRange(); | |
| if (!range.begin().file()) | |
| return true; | |
| return allowlist->find(range.begin().file()->name()) != allowlist->end(); | |
| } |