Add starlark files to depfile Change-Id: I07a63af85ca4124e4344900e33819af06a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/27120 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/integration_tests/simple/goldens/build.ninja.d b/integration_tests/simple/goldens/build.ninja.d index 84b21be..8b9a3e3 100644 --- a/integration_tests/simple/goldens/build.ninja.d +++ b/integration_tests/simple/goldens/build.ninja.d
@@ -1 +1 @@ -build.ninja.stamp: ../.gn ../BUILD.gn ../build/BUILD.gn ../build/BUILDCONFIG.gn ../build/toolchain/BUILD.gn ./args.gn \ No newline at end of file +build.ninja.stamp: ../.gn ../BUILD.gn ../build/BUILD.gn ../build/BUILDCONFIG.gn ../build/toolchain/BUILD.gn ./args.gn
diff --git a/integration_tests/starlark/goldens/build.ninja.d b/integration_tests/starlark/goldens/build.ninja.d index d66d296..f22feae 100644 --- a/integration_tests/starlark/goldens/build.ninja.d +++ b/integration_tests/starlark/goldens/build.ninja.d
@@ -1 +1 @@ -build.ninja.stamp: ../.gn ../BUILD.gn ../build/BUILDCONFIG.gn ../build/toolchain/BUILD.gn ./args.gn \ No newline at end of file +build.ninja.stamp: ../.gn ../BUILD.gn ../build/BUILDCONFIG.gn ../build/toolchain/BUILD.gn ../filegroup.scl ./args.gn
diff --git a/src/gn/build_settings.h b/src/gn/build_settings.h index 8c46a6a..08154b6 100644 --- a/src/gn/build_settings.h +++ b/src/gn/build_settings.h
@@ -157,6 +157,7 @@ } const Session& starlark_session() const; + bool has_starlark_session() const { return starlark_session_.has_value(); } // A list of target label patterns that can use allow_circular_includes_from. // If the returned pointer is null, allow_circular_includes_from may be used
diff --git a/src/gn/ffi/bridge.cc b/src/gn/ffi/bridge.cc index 99a0f95..b500afc 100644 --- a/src/gn/ffi/bridge.cc +++ b/src/gn/ffi/bridge.cc
@@ -17,10 +17,12 @@ #include "gn/target.h" #include "gn/test_with_scope.h" #include "gn/value.h" +#include <algorithm> #include <array> #include <cassert> #include <cstddef> #include <cstdint> +#include <initializer_list> #include <iterator> #include <memory> #include <new> @@ -665,6 +667,251 @@ Box<T>::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_BITCOPY_T +#define CXXBRIDGE1_RUST_BITCOPY_T +struct unsafe_bitcopy_t final { + explicit unsafe_bitcopy_t() = default; +}; +#endif // CXXBRIDGE1_RUST_BITCOPY_T + +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template <typename T> +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list<T>); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) & noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template <typename... Args> + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice<T>::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice<const T>::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; + +template <typename T> +Vec<T>::Vec(std::initializer_list<T> init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); +} + +template <typename T> +Vec<T>::~Vec() noexcept { + this->drop(); +} + +template <typename T> +Vec<T> &Vec<T>::operator=(Vec &&other) & noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template <typename T> +Vec<T> &Vec<T>::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; +} + +template <typename T> +bool Vec<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T *Vec<T>::data() noexcept { + return const_cast<T *>(const_cast<const Vec<T> *>(this)->data()); +} + +template <typename T> +const T &Vec<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<const char *>(this->data()); + return *reinterpret_cast<const T *>(data + n * size_of<T>()); +} + +template <typename T> +const T &Vec<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +const T &Vec<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +const T &Vec<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +T &Vec<T>::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<char *>(this->data()); + return *reinterpret_cast<T *>(data + n * size_of<T>()); +} + +template <typename T> +T &Vec<T>::at(std::size_t n) { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Vec<T>::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Vec<T>::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +void Vec<T>::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template <typename T> +void Vec<T>::push_back(const T &value) { + this->emplace_back(value); +} + +template <typename T> +void Vec<T>::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template <typename T> +template <typename... Args> +void Vec<T>::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) + + size * size_of<T>())) + T(std::forward<Args>(args)...); + this->set_len(size + 1); +} + +template <typename T> +void Vec<T>::clear() { + this->truncate(0); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::begin() noexcept { + return Slice<T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::end() noexcept { + return Slice<T>(this->data(), this->size()).end(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::begin() const noexcept { + return this->cbegin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::end() const noexcept { + return this->cend(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept { + return Slice<const T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cend() const noexcept { + return Slice<const T>(this->data(), this->size()).end(); +} + +template <typename T> +void Vec<T>::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +template <typename T> +Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + #ifndef CXXBRIDGE1_RUST_OPAQUE #define CXXBRIDGE1_RUST_OPAQUE class Opaque { @@ -781,11 +1028,11 @@ template <> class impl<Str> final { public: - static Str new_unchecked(repr::Fat repr) noexcept { - Str str = Str::uninit{}; - str.repr = repr; - return str; - } + static Str new_unchecked(repr::Fat repr) noexcept { + Str str = Str::uninit{}; + str.repr = repr; + return str; + } static repr::Fat repr(Str str) noexcept { return str.repr; } @@ -902,8 +1149,7 @@ #ifndef CXXBRIDGE1_STRUCT_RustTarget #define CXXBRIDGE1_STRUCT_RustTarget struct RustTarget final : public ::rust::Opaque { - ::rust::Str execute_rule_impl(::Session const& session, - ::Err& err) const noexcept; + ::rust::Str execute_rule_impl(::Session const &session, ::Err &err) const noexcept; ~RustTarget() = delete; private: @@ -922,6 +1168,7 @@ static ::rust::Box<::Session> new_for_testing() noexcept; ::RustTarget const ®ister_cxx_target(::Target const &target) const noexcept; void load_values(::rust::Str label, ::rust::Str relative_to, ::rust::Slice<::rust::Str const> keys, ::Scope &scope, ::Settings const &settings, ::ParseNodePtr origin, ::Err &err) const noexcept; + ::rust::Vec<::rust::Str> loaded() const noexcept; ~Session() = delete; private: @@ -1062,12 +1309,9 @@ new (return$) ::std::vector<::SourceFile> const *(&(self.*public_headers$)()); } -void cxxbridge1$196$Target$computed_outputs( - ::Target const& self, - ::std::vector<::OutputFile> const** return$) noexcept { - ::std::vector<::OutputFile> const& (::Target::*computed_outputs$)() const = - &::Target::computed_outputs; - new (return$)::std::vector<::OutputFile> const*(&(self.*computed_outputs$)()); +void cxxbridge1$196$Target$computed_outputs(::Target const &self, ::std::vector<::OutputFile> const **return$) noexcept { + ::std::vector<::OutputFile> const &(::Target::*computed_outputs$)() const = &::Target::computed_outputs; + new (return$) ::std::vector<::OutputFile> const *(&(self.*computed_outputs$)()); } ::RustTarget const *cxxbridge1$196$Target$rust_target(::Target const &self, ::Session const &session) noexcept { @@ -1232,8 +1476,7 @@ ::std::size_t cxxbridge1$196$RustTarget$operator$sizeof() noexcept; ::std::size_t cxxbridge1$196$RustTarget$operator$alignof() noexcept; -::rust::repr::Fat cxxbridge1$196$RustTarget$execute_rule_impl( - ::RustTarget const& self, ::Session const& session, ::Err& err) noexcept; +::rust::repr::Fat cxxbridge1$196$RustTarget$execute_rule_impl(::RustTarget const &self, ::Session const &session, ::Err &err) noexcept; ::std::size_t cxxbridge1$196$Session$operator$sizeof() noexcept; ::std::size_t cxxbridge1$196$Session$operator$alignof() noexcept; @@ -1244,6 +1487,8 @@ ::RustTarget const *cxxbridge1$196$Session$register_cxx_target(::Session const &self, ::Target const &target) noexcept; void cxxbridge1$196$Session$load_values(::Session const &self, ::rust::Str label, ::rust::Str relative_to, ::rust::Slice<::rust::Str const> keys, ::Scope &scope, ::Settings const &settings, ::ParseNodePtr *origin, ::Err &err) noexcept; + +void cxxbridge1$196$Session$loaded(::Session const &self, ::rust::Vec<::rust::Str> *return$) noexcept; ::std::size_t cxxbridge1$196$OwnedFrozenValue$operator$sizeof() noexcept; ::std::size_t cxxbridge1$196$OwnedFrozenValue$operator$alignof() noexcept; @@ -1264,10 +1509,8 @@ return cxxbridge1$196$RustTarget$operator$alignof(); } -::rust::Str RustTarget::execute_rule_impl(::Session const& session, - ::Err& err) const noexcept { - return ::rust::impl<::rust::Str>::new_unchecked( - cxxbridge1$196$RustTarget$execute_rule_impl(*this, session, err)); +::rust::Str RustTarget::execute_rule_impl(::Session const &session, ::Err &err) const noexcept { + return ::rust::impl<::rust::Str>::new_unchecked(cxxbridge1$196$RustTarget$execute_rule_impl(*this, session, err)); } ::std::size_t Session::layout::size() noexcept { @@ -1295,6 +1538,12 @@ cxxbridge1$196$Session$load_values(*this, label, relative_to, keys, scope, settings, &origin$.value, err); } +::rust::Vec<::rust::Str> Session::loaded() const noexcept { + ::rust::MaybeUninit<::rust::Vec<::rust::Str>> return$; + cxxbridge1$196$Session$loaded(*this, &return$.value); + return ::std::move(return$.value); +} + ::std::size_t OwnedFrozenValue::layout::size() noexcept { return cxxbridge1$196$OwnedFrozenValue$operator$sizeof(); } @@ -1410,58 +1659,38 @@ ::rust::deleter_if<::rust::detail::is_complete<::std::vector<::SourceFile>>::value>{}(ptr); } -::std::vector<::OutputFile>* cxxbridge1$std$vector$OutputFile$new() noexcept { +::std::vector<::OutputFile> *cxxbridge1$std$vector$OutputFile$new() noexcept { return new ::std::vector<::OutputFile>(); } -::std::size_t cxxbridge1$std$vector$OutputFile$size( - ::std::vector<::OutputFile> const& s) noexcept { +::std::size_t cxxbridge1$std$vector$OutputFile$size(::std::vector<::OutputFile> const &s) noexcept { return s.size(); } -::std::size_t cxxbridge1$std$vector$OutputFile$capacity( - ::std::vector<::OutputFile> const& s) noexcept { +::std::size_t cxxbridge1$std$vector$OutputFile$capacity(::std::vector<::OutputFile> const &s) noexcept { return s.capacity(); } -::OutputFile* cxxbridge1$std$vector$OutputFile$get_unchecked( - ::std::vector<::OutputFile>* s, ::std::size_t pos) noexcept { +::OutputFile *cxxbridge1$std$vector$OutputFile$get_unchecked(::std::vector<::OutputFile> *s, ::std::size_t pos) noexcept { return &(*s)[pos]; } -bool cxxbridge1$std$vector$OutputFile$reserve(::std::vector<::OutputFile>* s, - ::std::size_t new_cap) noexcept { +bool cxxbridge1$std$vector$OutputFile$reserve(::std::vector<::OutputFile> *s, ::std::size_t new_cap) noexcept { return ::rust::if_move_constructible<::OutputFile>::reserve(*s, new_cap); } -static_assert( - ::rust::detail::is_complete< - ::std::remove_extent<::std::vector<::OutputFile>>::type>::value, - "definition of `::std::vector<::OutputFile>` is required"); -static_assert(sizeof(::std::unique_ptr<::std::vector<::OutputFile>>) == - sizeof(void*), - ""); -static_assert(alignof(::std::unique_ptr<::std::vector<::OutputFile>>) == - alignof(void*), - ""); -void cxxbridge1$unique_ptr$std$vector$OutputFile$null( - ::std::unique_ptr<::std::vector<::OutputFile>>* ptr) noexcept { - ::new (ptr)::std::unique_ptr<::std::vector<::OutputFile>>(); +static_assert(::rust::detail::is_complete<::std::remove_extent<::std::vector<::OutputFile>>::type>::value, "definition of `::std::vector<::OutputFile>` is required"); +static_assert(sizeof(::std::unique_ptr<::std::vector<::OutputFile>>) == sizeof(void *), ""); +static_assert(alignof(::std::unique_ptr<::std::vector<::OutputFile>>) == alignof(void *), ""); +void cxxbridge1$unique_ptr$std$vector$OutputFile$null(::std::unique_ptr<::std::vector<::OutputFile>> *ptr) noexcept { + ::new (ptr) ::std::unique_ptr<::std::vector<::OutputFile>>(); } -void cxxbridge1$unique_ptr$std$vector$OutputFile$raw( - ::std::unique_ptr<::std::vector<::OutputFile>>* ptr, - ::std::unique_ptr<::std::vector<::OutputFile>>::pointer raw) noexcept { - ::new (ptr)::std::unique_ptr<::std::vector<::OutputFile>>(raw); +void cxxbridge1$unique_ptr$std$vector$OutputFile$raw(::std::unique_ptr<::std::vector<::OutputFile>> *ptr, ::std::unique_ptr<::std::vector<::OutputFile>>::pointer raw) noexcept { + ::new (ptr) ::std::unique_ptr<::std::vector<::OutputFile>>(raw); } -::std::unique_ptr<::std::vector<::OutputFile>>::element_type const* -cxxbridge1$unique_ptr$std$vector$OutputFile$get( - ::std::unique_ptr<::std::vector<::OutputFile>> const& ptr) noexcept { +::std::unique_ptr<::std::vector<::OutputFile>>::element_type const *cxxbridge1$unique_ptr$std$vector$OutputFile$get(::std::unique_ptr<::std::vector<::OutputFile>> const &ptr) noexcept { return ptr.get(); } -::std::unique_ptr<::std::vector<::OutputFile>>::pointer -cxxbridge1$unique_ptr$std$vector$OutputFile$release( - ::std::unique_ptr<::std::vector<::OutputFile>>& ptr) noexcept { +::std::unique_ptr<::std::vector<::OutputFile>>::pointer cxxbridge1$unique_ptr$std$vector$OutputFile$release(::std::unique_ptr<::std::vector<::OutputFile>> &ptr) noexcept { return ptr.release(); } -void cxxbridge1$unique_ptr$std$vector$OutputFile$drop( - ::std::unique_ptr<::std::vector<::OutputFile>>* ptr) noexcept { - ::rust::deleter_if< - ::rust::detail::is_complete<::std::vector<::OutputFile>>::value>{}(ptr); +void cxxbridge1$unique_ptr$std$vector$OutputFile$drop(::std::unique_ptr<::std::vector<::OutputFile>> *ptr) noexcept { + ::rust::deleter_if<::rust::detail::is_complete<::std::vector<::OutputFile>>::value>{}(ptr); } static_assert(::rust::detail::is_complete<::std::remove_extent<::Scope>::type>::value, "definition of `::Scope` is required");
diff --git a/src/gn/ffi/bridge.h b/src/gn/ffi/bridge.h index 85884f6..0ad1e9a 100644 --- a/src/gn/ffi/bridge.h +++ b/src/gn/ffi/bridge.h
@@ -18,10 +18,12 @@ #include "gn/target.h" #include "gn/test_with_scope.h" #include "gn/value.h" +#include <algorithm> #include <array> #include <cassert> #include <cstddef> #include <cstdint> +#include <initializer_list> #include <iterator> #include <memory> #include <new> @@ -658,6 +660,251 @@ Box<T>::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_BITCOPY_T +#define CXXBRIDGE1_RUST_BITCOPY_T +struct unsafe_bitcopy_t final { + explicit unsafe_bitcopy_t() = default; +}; +#endif // CXXBRIDGE1_RUST_BITCOPY_T + +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template <typename T> +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list<T>); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) & noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template <typename... Args> + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice<T>::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice<const T>::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; + +template <typename T> +Vec<T>::Vec(std::initializer_list<T> init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); +} + +template <typename T> +Vec<T>::~Vec() noexcept { + this->drop(); +} + +template <typename T> +Vec<T> &Vec<T>::operator=(Vec &&other) & noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template <typename T> +Vec<T> &Vec<T>::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; +} + +template <typename T> +bool Vec<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T *Vec<T>::data() noexcept { + return const_cast<T *>(const_cast<const Vec<T> *>(this)->data()); +} + +template <typename T> +const T &Vec<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<const char *>(this->data()); + return *reinterpret_cast<const T *>(data + n * size_of<T>()); +} + +template <typename T> +const T &Vec<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +const T &Vec<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +const T &Vec<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +T &Vec<T>::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<char *>(this->data()); + return *reinterpret_cast<T *>(data + n * size_of<T>()); +} + +template <typename T> +T &Vec<T>::at(std::size_t n) { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Vec<T>::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Vec<T>::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +void Vec<T>::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template <typename T> +void Vec<T>::push_back(const T &value) { + this->emplace_back(value); +} + +template <typename T> +void Vec<T>::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template <typename T> +template <typename... Args> +void Vec<T>::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) + + size * size_of<T>())) + T(std::forward<Args>(args)...); + this->set_len(size + 1); +} + +template <typename T> +void Vec<T>::clear() { + this->truncate(0); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::begin() noexcept { + return Slice<T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::end() noexcept { + return Slice<T>(this->data(), this->size()).end(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::begin() const noexcept { + return this->cbegin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::end() const noexcept { + return this->cend(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept { + return Slice<const T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cend() const noexcept { + return Slice<const T>(this->data(), this->size()).end(); +} + +template <typename T> +void Vec<T>::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +template <typename T> +Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + #ifndef CXXBRIDGE1_RUST_OPAQUE #define CXXBRIDGE1_RUST_OPAQUE class Opaque { @@ -820,8 +1067,7 @@ #ifndef CXXBRIDGE1_STRUCT_RustTarget #define CXXBRIDGE1_STRUCT_RustTarget struct RustTarget final : public ::rust::Opaque { - ::rust::Str execute_rule_impl(::Session const& session, - ::Err& err) const noexcept; + ::rust::Str execute_rule_impl(::Session const &session, ::Err &err) const noexcept; ~RustTarget() = delete; private: @@ -840,6 +1086,7 @@ static ::rust::Box<::Session> new_for_testing() noexcept; ::RustTarget const ®ister_cxx_target(::Target const &target) const noexcept; void load_values(::rust::Str label, ::rust::Str relative_to, ::rust::Slice<::rust::Str const> keys, ::Scope &scope, ::Settings const &settings, ::ParseNodePtr origin, ::Err &err) const noexcept; + ::rust::Vec<::rust::Str> loaded() const noexcept; ~Session() = delete; private:
diff --git a/src/gn/ffi/session.cc b/src/gn/ffi/session.cc index b2329bf..7143c6b 100644 --- a/src/gn/ffi/session.cc +++ b/src/gn/ffi/session.cc
@@ -9,6 +9,7 @@ #include "gn/build_settings.h" #include "gn/err.h" #include "gn/ffi/bridge.h" +#include "gn/filesystem_utils.h" #include "gn/functions.h" #include "gn/parse_tree.h" #include "gn/scope.h" @@ -76,3 +77,12 @@ return !err.has_error(); } + +void add_all_loaded_files(const Session& session, + std::vector<base::FilePath>& files) { + const auto& loaded = session.loaded(); + files.reserve(files.size() + loaded.size()); + for (const auto& file : loaded) { + files.push_back(UTF8ToFilePath(std::string_view(file))); + } +} \ No newline at end of file
diff --git a/src/gn/ffi/session.h b/src/gn/ffi/session.h index 75839c2..56df842 100644 --- a/src/gn/ffi/session.h +++ b/src/gn/ffi/session.h
@@ -6,6 +6,9 @@ #define TOOLS_GN_FFI_SESSION_H_ #include <span> +#include <vector> + +#include "base/files/file_path.h" class Err; class ParseNode; @@ -35,4 +38,10 @@ ParseNodePtr parse_node, Err& err); +// Collects all files loaded by the session and adds them to files. +// Note that this specifically refers to *starlark* files. +// Returned file paths will be relaltive and normalized to '/' separators. +void add_all_loaded_files(const Session& session, + std::vector<base::FilePath>& files); + #endif // TOOLS_GN_FFI_SESSION_H_ \ No newline at end of file
diff --git a/src/gn/input_file_manager.cc b/src/gn/input_file_manager.cc index 6e2f072..83e9c6d 100644 --- a/src/gn/input_file_manager.cc +++ b/src/gn/input_file_manager.cc
@@ -264,13 +264,17 @@ return static_cast<int>(input_files_.size()); } -void InputFileManager::AddAllPhysicalInputFileNamesToVectorSetSorter( - VectorSetSorter<base::FilePath>* sorter) const { +void InputFileManager::AddAllPhysicalInputFiles( + std::vector<base::FilePath>& files) const { std::lock_guard<std::mutex> lock(lock_); + files.reserve(files.size() + input_files_.size()); + for (const auto& file : input_files_) { - if (!file.second->file.physical_name().empty()) - sorter->Add(file.second->file.physical_name()); + const base::FilePath& physical = file.second->file.physical_name(); + if (!physical.empty()) { + files.push_back(physical); + } } }
diff --git a/src/gn/input_file_manager.h b/src/gn/input_file_manager.h index 5d82eab..b202250 100644 --- a/src/gn/input_file_manager.h +++ b/src/gn/input_file_manager.h
@@ -91,16 +91,8 @@ // Does not count dynamic input. int GetInputFileCount() const; - // Add all physical input files to a VectorSetSorter instance. - // This allows fast merging and sorting with other file paths sets. - // - // This is more memory efficient than returning a vector of base::FilePath - // instance, especially with projects with a very large number of input files, - // but note that the VectorSetSorter only holds pointers to the - // items recorded in this InputFileManager instance, and it is up to the - // caller to ensure these will not change until the sorter is destroyed. - void AddAllPhysicalInputFileNamesToVectorSetSorter( - VectorSetSorter<base::FilePath>* sorter) const; + // Adds all physical input files to the given vector. + void AddAllPhysicalInputFiles(std::vector<base::FilePath>& files) const; void set_load_file_callback(SyncLoadFileCallback load_file_callback) { load_file_callback_ = load_file_callback;
diff --git a/src/gn/json_project_writer.cc b/src/gn/json_project_writer.cc index 79df92e..8501561 100644 --- a/src/gn/json_project_writer.cc +++ b/src/gn/json_project_writer.cc
@@ -13,16 +13,20 @@ #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/json/string_escape.h" +#include "gn/build_settings.h" #include "gn/builder.h" #include "gn/commands.h" #include "gn/deps_iterator.h" #include "gn/desc_builder.h" +#include "gn/ffi/bridge.h" +#include "gn/ffi/session.h" #include "gn/filesystem_utils.h" #include "gn/invoke_python.h" #include "gn/resolved_target_data.h" #include "gn/scheduler.h" #include "gn/settings.h" #include "gn/string_output_buffer.h" +#include "gn/value.h" #include "util/worker_pool.h" // Structure of JSON output file @@ -358,27 +362,42 @@ // Other files read by the build. std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies(); + g_scheduler->input_file_manager()->AddAllPhysicalInputFiles(other_files); - const InputFileManager* input_file_manager = - g_scheduler->input_file_manager(); - - VectorSetSorter<base::FilePath> sorter( - input_file_manager->GetInputFileCount() + other_files.size()); - - input_file_manager->AddAllPhysicalInputFileNamesToVectorSetSorter(&sorter); - - sorter.Add(other_files.begin(), other_files.end()); - - std::string build_path = FilePathToUTF8(build_settings->root_path()); - auto item_callback = [&json_writer, - &build_path](const base::FilePath& input_file) { + std::vector<SourceFile> files; + const std::string root_path = FilePathToUTF8(build_settings->root_path()); + for (const auto& other_file : other_files) { std::string file; if (MakeAbsolutePathRelativeIfPossible( - build_path, FilePathToUTF8(input_file), &file)) { - json_writer.AddListItem(file); + root_path, FilePathToUTF8(other_file), &file)) { + files.emplace_back(std::move(file)); } - }; - sorter.IterateOver(item_callback); + } + + if (build_settings->has_starlark_session()) { + std::vector<base::FilePath> loaded_files; + add_all_loaded_files(build_settings->starlark_session(), loaded_files); + for (const auto& loaded_file : loaded_files) { + Err err; + SourceFile source_file = + build_settings->build_dir().ResolveRelativeFile( + Value(nullptr, FilePathToUTF8(loaded_file)), &err, + build_settings->root_path_utf8()); + // Starlark intentionally doesn't support loading files outside of the + // source root. + DCHECK(!err.has_error()); + DCHECK(source_file.is_source_absolute()); + files.push_back(std::move(source_file)); + } + } + + std::ranges::sort(files); + auto [first, last] = std::ranges::unique(files); + files.erase(first, last); + + for (const auto& file : files) { + json_writer.AddListItem(file.value()); + } json_writer.EndList(); // gen_input_files
diff --git a/src/gn/ninja_build_writer.cc b/src/gn/ninja_build_writer.cc index 12e323f..b298f04 100644 --- a/src/gn/ninja_build_writer.cc +++ b/src/gn/ninja_build_writer.cc
@@ -19,6 +19,8 @@ #include "gn/builder.h" #include "gn/err.h" #include "gn/escape.h" +#include "gn/ffi/bridge.h" +#include "gn/ffi/session.h" #include "gn/filesystem_utils.h" #include "gn/input_file_manager.h" #include "gn/loader.h" @@ -352,33 +354,31 @@ dep_out_ << "build.ninja.stamp:"; // Other files read by the build. - std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies(); - - const InputFileManager* input_file_manager = - g_scheduler->input_file_manager(); - - VectorSetSorter<base::FilePath> sorter( - input_file_manager->GetInputFileCount() + other_files.size()); - - input_file_manager->AddAllPhysicalInputFileNamesToVectorSetSorter(&sorter); - sorter.Add(other_files.begin(), other_files.end()); + std::vector<base::FilePath> files = g_scheduler->GetGenDependencies(); + g_scheduler->input_file_manager()->AddAllPhysicalInputFiles(files); const base::FilePath build_path = build_settings_->build_dir().Resolve(build_settings_->root_path()); + for (auto& file : files) { + file = MakeAbsoluteFilePathRelativeIfPossible(build_path, file) + .NormalizePathSeparatorsTo('/'); + } + + if (build_settings_->has_starlark_session()) { + add_all_loaded_files(build_settings_->starlark_session(), files); + } + + std::sort(files.begin(), files.end()); + files.erase(std::unique(files.begin(), files.end()), files.end()); + EscapeOptions depfile_escape; depfile_escape.mode = ESCAPE_DEPFILE; - auto item_callback = [this, &depfile_escape, - &build_path](const base::FilePath& input_file) { - const base::FilePath file = - MakeAbsoluteFilePathRelativeIfPossible(build_path, input_file); + for (const auto& file : files) { dep_out_ << " "; - EscapeStringToStream(dep_out_, - FilePathToUTF8(file.NormalizePathSeparatorsTo('/')), - depfile_escape); - }; - - sorter.IterateOver(item_callback); + EscapeStringToStream(dep_out_, FilePathToUTF8(file), depfile_escape); + } + dep_out_ << "\n"; out_ << std::endl; }
diff --git a/src/gn/ninja_build_writer_unittest.cc b/src/gn/ninja_build_writer_unittest.cc index 493ba40..9625d4b 100644 --- a/src/gn/ninja_build_writer_unittest.cc +++ b/src/gn/ninja_build_writer_unittest.cc
@@ -294,7 +294,7 @@ ASSERT_TRUE(writer.Run(&err)); EXPECT_EQ(depfile_out.str(), - "build.ninja.stamp: ../../path\\ with\\ space/BUILD.gn"); + "build.ninja.stamp: ../../path\\ with\\ space/BUILD.gn\n"); } TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
diff --git a/src/gn/starlark/crates/attr/src/allow_files.rs b/src/gn/starlark/crates/attr/src/allow_files.rs index ad5e71e..1806c99 100644 --- a/src/gn/starlark/crates/attr/src/allow_files.rs +++ b/src/gn/starlark/crates/attr/src/allow_files.rs
@@ -91,7 +91,7 @@ } else { // It's a file. allow_files.validate(s)?; - LabelOrFile::File(path_resolver.source_file(relative_to, s)?) + LabelOrFile::File(path_resolver.existing_source_file(relative_to, s)?) }, ) }
diff --git a/src/gn/starlark/crates/attr/src/attr.rs b/src/gn/starlark/crates/attr/src/attr.rs index 2e24128..375a1b5 100644 --- a/src/gn/starlark/crates/attr/src/attr.rs +++ b/src/gn/starlark/crates/attr/src/attr.rs
@@ -385,7 +385,7 @@ assert_eq!( Attr::create(&schema, Some(heap.alloc("file.cc")), pkg, &path_resolver,).unwrap(), Attr::Label(Some(LabelOrFile::File( - path_resolver.source_file(pkg, "file.cc").unwrap() + path_resolver.existing_source_file(pkg, "file.cc").unwrap() ))) );
diff --git a/src/gn/starlark/crates/ffi/src/bridge.rs b/src/gn/starlark/crates/ffi/src/bridge.rs index 5cb17d2..29da1af 100644 --- a/src/gn/starlark/crates/ffi/src/bridge.rs +++ b/src/gn/starlark/crates/ffi/src/bridge.rs
@@ -353,6 +353,8 @@ err: Pin<&mut Err>, ); + fn loaded(self: &Session) -> Vec<&'static str>; + type OwnedFrozenValue; #[rust_name = "clone_cxx"] fn clone(self: &OwnedFrozenValue) -> Box<OwnedFrozenValue>;
diff --git a/src/gn/starlark/crates/ffi/src/session.rs b/src/gn/starlark/crates/ffi/src/session.rs index 8c7c0c6..3c04e99 100644 --- a/src/gn/starlark/crates/ffi/src/session.rs +++ b/src/gn/starlark/crates/ffi/src/session.rs
@@ -54,10 +54,11 @@ impl Session { /// Creates a new `Session`. pub fn from_resolver(path_resolver: PathResolver) -> Self { - let loader = FileLoader::default(); - loader.preload(rule::register_builtin_rules::< - crate::eval_context::EvalContext, - >()); + let mut loader = FileLoader::default(); + loader.preload( + Label::parse("//builtins:rules.scl", PackageRef::root()).unwrap(), + rule::register_builtin_rules::<crate::eval_context::EvalContext>(), + ); Self { loader, path_resolver, @@ -80,6 +81,18 @@ Box::new(Self::from_resolver(PathResolver::new_for_testing())) } + pub fn loaded(&self) -> Vec<&'static str> { + self.loader + .loaded() + .into_iter() + .map(|label| { + self.path_resolver + .source_file(label.package(), label.name()) + .as_str() + }) + .collect() + } + pub(crate) fn register_target(&self, target: Target) -> crate::TargetRef { let label = target.label().as_ref().to_owned(); let toolchain = target.toolchain().to_owned();
diff --git a/src/gn/starlark/crates/loader/src/loader.rs b/src/gn/starlark/crates/loader/src/loader.rs index 26ef7ef..1381d69 100644 --- a/src/gn/starlark/crates/loader/src/loader.rs +++ b/src/gn/starlark/crates/loader/src/loader.rs
@@ -16,7 +16,7 @@ syntax::{AstModule, Dialect}, values::FrozenHeapName, }; -use types::{EvalContext, EvaluatorContextExt as _, LabelRef, PackageRef, PathResolver}; +use types::{EvalContext, EvaluatorContextExt as _, Label, LabelRef, PackageRef, PathResolver}; use crate::Error; @@ -33,7 +33,7 @@ wait: Arc<Condvar>, // What needs to finish evaluating before this can be evaluated. // This is used purely for cycle detection. - needs: Option<String>, + needs: Option<Label>, }, Loaded(starlark::Result<Pin<Box<FrozenModule>>>), } @@ -51,17 +51,19 @@ /// (`.bzl` files). #[derive(Default)] pub struct FileLoader { - files: RwLock<HashMap<String, Arc<Mutex<FileStatus>>>>, + files: RwLock<HashMap<Label, Arc<Mutex<FileStatus>>>>, + preloaded: Vec<Label>, } impl FileLoader { /// Preloads a frozen module into the loader's cache. /// This should be called once for each builtin module. /// By convention, builtin modules will be named "//builtins:$NAME.scl" - pub fn preload(&self, module: FrozenModule) { + pub fn preload(&mut self, label: Label, module: FrozenModule) { let mut files = self.files.write().unwrap(); + self.preloaded.push(label.clone()); files.insert( - module.frozen_heap().name().unwrap().to_string(), + label, Arc::new(Mutex::new(FileStatus::Loaded(Ok(Box::pin(module))))), ); } @@ -114,11 +116,9 @@ globals: &Globals, make_eval_context: &F, ) -> starlark::Result<FrozenModule> { - // Starlark-rs requires module identifiers to be strings. - let label_str = label.to_string(); let file_status = { let mut loader = self.files.write().unwrap(); - match loader.entry(label_str.clone()) { + match loader.entry(label.to_owned()) { Entry::Occupied(entry) => { let file_status = entry.get().clone(); drop(loader); @@ -131,7 +131,6 @@ let result = self.load_and_evaluate( label, - &label_str, &file_status, path_resolver, globals, @@ -144,7 +143,6 @@ fn load_and_evaluate<'b, C: EvalContext, F: Fn(&PackageRef) -> C>( &self, label: LabelRef<'b>, - label_str: &str, file_status: &Arc<Mutex<FileStatus>>, path_resolver: &PathResolver, globals: &Globals, @@ -152,17 +150,19 @@ ) -> starlark::Result<FrozenModule> { // Read and parse the file to get its dependencies. let absolute_path = path_resolver.absolute_path(label.package(), label.name()); - let content = fs::read_to_string(&absolute_path) - .map_err(|_| Error::ReadFailed(label_str.to_owned()))?; - let ast = AstModule::parse(label_str, content, &BZL_FILE_DIALECT)?; + let content = + fs::read_to_string(&absolute_path).map_err(|_| Error::ReadFailed(label.to_string()))?; + let ast = AstModule::parse(&label.to_string(), content, &BZL_FILE_DIALECT)?; let mut deps: Vec<(String, FrozenModule)> = Default::default(); if !ast.loads().is_empty() { for load in ast.loads() { let dep_label = types::Label::parse(load.module_id, label.package())?; - let dep_label_str = dep_label.to_string(); - if let Some(cycle) = self.find_cycle_path(file_status, &dep_label_str) { - return Err(Error::CycleDetected(cycle).into()); + if let Some(cycle) = self.find_cycle_path(file_status, &dep_label) { + return Err(Error::CycleDetected( + cycle.into_iter().map(|l| l.to_string()).collect(), + ) + .into()); } deps.push(( @@ -189,15 +189,15 @@ eval.set_loader(&loader); eval.eval_module(ast, globals)?; } - Ok(module.freeze_named(FrozenHeapName::user(label_str.to_owned()))?) + Ok(module.freeze_named(FrozenHeapName::user(label.to_string()))?) }) } fn find_cycle_path( &self, current: &Arc<Mutex<FileStatus>>, - target: &str, - ) -> Option<Vec<String>> { + target: &Label, + ) -> Option<Vec<Label>> { // Set the dependency before we start doing cycle detection. // This prevents multiple threads simultaneously calling find_cycle_path // not seeing a cycle on each other, then adding a dependency on each @@ -209,10 +209,10 @@ // currently trying to load. unreachable!(); }; - *needs = Some(target.to_owned()); + *needs = Some(target.clone()); } let loader = self.files.read().unwrap(); - let mut cur = target.to_owned(); + let mut cur = target.clone(); let mut cycle = vec![cur.clone()]; while let Some(status_mutex) = loader.get(&cur) { let status = status_mutex.lock().unwrap(); @@ -231,6 +231,16 @@ } None } + + /// Returns a list of all labels explicitly loaded by the loader + pub fn loaded(&self) -> Vec<Label> { + let files = self.files.read().unwrap(); + files + .keys() + .filter(|k| !self.preloaded.contains(k)) + .cloned() + .collect() + } } /// Helper loader to load preloaded dependencies during evaluator execution. @@ -253,7 +263,6 @@ use std::rc::Rc; use testutils::{FakeEvalContext, FakeSession}; - use types::Label; use super::*;
diff --git a/src/gn/starlark/crates/types/src/path_resolver.rs b/src/gn/starlark/crates/types/src/path_resolver.rs index d1eef0b..9cdf05d 100644 --- a/src/gn/starlark/crates/types/src/path_resolver.rs +++ b/src/gn/starlark/crates/types/src/path_resolver.rs
@@ -55,23 +55,28 @@ } /// Creates a `File` object for a file path relative to a package. - /// Validates that the file exists on disk. - pub fn source_file(&self, pkg: &PackageRef, s: &str) -> starlark::Result<File> { - // Gn *does not* check that the files you refer to exist on disk. - // This is because native GN allows referencing generated files that do - // not yet exist at `gn gen` time. - // We explicitly disallow referencing generated files in starlark, so - // we should validate that the files exist on disk for correctness. - let abs_path = self.absolute_path(pkg, s); - if !abs_path.exists() { - return Err(crate::Error::FileNotFound(pkg.to_owned(), s.to_owned()).into()); - } + pub fn source_file(&self, pkg: &PackageRef, s: &str) -> File { let pkg_dir = pkg.as_source_relative(); let rel_path = if pkg.is_root() { format!("{}{s}", self.source_root_rel) } else { format!("{}{pkg_dir}/{s}", self.source_root_rel) }; - Ok(File::intern(&rel_path)) + File::intern(&rel_path) + } + + /// Creates a `File` object for a file path relative to a package. + /// Validates that the file exists on disk. + pub fn existing_source_file(&self, pkg: &PackageRef, s: &str) -> starlark::Result<File> { + // Gn *does not* check that the files you refer to exist on disk. + // This is because native GN allows referencing generated files that do + // not yet exist at `gn gen` time. + // We explicitly disallow referencing generated files in starlark, so + // we should validate that the files exist on disk for correctness. + if self.absolute_path(pkg, s).exists() { + Ok(self.source_file(pkg, s)) + } else { + Err(crate::Error::FileNotFound(pkg.to_owned(), s.to_owned()).into()) + } } }