Rework API to create targets. Targets creation will now follow the same API regardless of whether the target is a native GN target or a starlark target. This requires us to pass a scope-like object to GN. Bug: 528225104 Change-Id: I8bec09858d2d500dfa9e5e9b1ed2efad6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24180 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/ffi/bridge.cc b/src/gn/ffi/bridge.cc index e7eb883..792758f 100644 --- a/src/gn/ffi/bridge.cc +++ b/src/gn/ffi/bridge.cc
@@ -605,6 +605,11 @@ new (return$) ::SliceAny(GetScopeItems$(scope)); } +::Value const *cxxbridge1$196$GetValue(::Scope const &scope, ::rust::Str ident) noexcept { + ::Value const *(*GetValue$)(::Scope const &, ::rust::Str) = ::GetValue; + return GetValue$(scope, ident); +} + ::Settings const *cxxbridge1$196$Scope$settings_cxx(::Scope const &self) noexcept { ::Settings const *(::Scope::*settings_cxx$)() const = &::Scope::settings; return (self.*settings_cxx$)();
diff --git a/src/gn/ffi/scope.cc b/src/gn/ffi/scope.cc index f3a658e..84abed9 100644 --- a/src/gn/ffi/scope.cc +++ b/src/gn/ffi/scope.cc
@@ -65,3 +65,8 @@ } return IntoSlice(std::move(vec)); } + +const Value* GetValue(const Scope& scope, rust::Str ident) { + std::string_view ident_sv(ident.data(), ident.size()); + return scope.GetValue(ident_sv); +}
diff --git a/src/gn/ffi/scope.h b/src/gn/ffi/scope.h index dc3e309..fd018b0 100644 --- a/src/gn/ffi/scope.h +++ b/src/gn/ffi/scope.h
@@ -10,6 +10,7 @@ #include "cxx.h" class Scope; +class Value; struct SliceAny; // Constructs a new child Scope, populates placeholder Values for the given @@ -26,4 +27,7 @@ // Safety: Rust is required to convert this to an OwnedSlice<KeyValue>. SliceAny GetScopeItems(const Scope& scope); +// Returns a pointer to the value in the scope or nullptr if not found. +const Value* GetValue(const Scope& scope, rust::Str ident); + #endif // TOOLS_GN_FFI_SCOPE_H_
diff --git a/src/gn/starlark/Cargo.lock b/src/gn/starlark/Cargo.lock index 8e1fb30..7095a02 100644 --- a/src/gn/starlark/Cargo.lock +++ b/src/gn/starlark/Cargo.lock
@@ -787,6 +787,12 @@ ] [[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] name = "home" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1599,6 +1605,28 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.119", +] + +[[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1711,6 +1739,7 @@ "cxx", "starlark", "starlark_derive", + "strum", "thiserror", ]
diff --git a/src/gn/starlark/Cargo.toml b/src/gn/starlark/Cargo.toml index 5d03132..dfc681c 100644 --- a/src/gn/starlark/Cargo.toml +++ b/src/gn/starlark/Cargo.toml
@@ -31,6 +31,7 @@ starlark = "0.14" starlark_derive = "0.14" thiserror = "2.0" +strum = { version = "0.26", features = ["derive"] } # A short-lived fork until my PR gets merged. # Basically all it does is make some pub(crate) things public, so that we can
diff --git a/src/gn/starlark/crates/attr/src/lib.rs b/src/gn/starlark/crates/attr/src/lib.rs index 5eb676c..1729bd7 100644 --- a/src/gn/starlark/crates/attr/src/lib.rs +++ b/src/gn/starlark/crates/attr/src/lib.rs
@@ -16,7 +16,7 @@ pub use attr::{Attr, LabelOrFile}; pub use cfg::AttrCfg; pub use ctx::{CtxAttr, CtxAttrSchema}; -pub use errors::Error; +pub(crate) use errors::Error; pub use globals::{AttrModule, AttrSpecArgs}; pub use schema::{AllowFilesSchema, AttrKind, AttrSchema}; pub use traits::{EvalContext, EvalContextAttrExt, Session, TargetRef};
diff --git a/src/gn/starlark/crates/attr/src/traits.rs b/src/gn/starlark/crates/attr/src/traits.rs index 87132af..7f67ad1 100644 --- a/src/gn/starlark/crates/attr/src/traits.rs +++ b/src/gn/starlark/crates/attr/src/traits.rs
@@ -3,13 +3,15 @@ // found in the LICENSE file. /// Re-export the traits from types so caller crates can access them seamlessly. -pub use types::{EvalContext, Session, TargetRef}; +pub use types::{EvalContext, OutputType, Session, TargetRef}; /// Extension trait for EvalContext to support target creation. pub trait EvalContextAttrExt: types::EvalContext { - fn create_starlark_target( + fn create_target( &self, + target_type: Option<OutputType>, target_name: &str, + scope: &Self::Scope, rule: starlark::values::FrozenValue, attrs: Vec<crate::Attr>, ) -> starlark::Result<<Self::Session as types::Session>::TargetRef>;
diff --git a/src/gn/starlark/crates/attr/src/value.rs b/src/gn/starlark/crates/attr/src/value.rs index af6a928..acdf29c 100644 --- a/src/gn/starlark/crates/attr/src/value.rs +++ b/src/gn/starlark/crates/attr/src/value.rs
@@ -525,7 +525,6 @@ target_label.clone(), FakeTargetRef::new(FakeTarget { outputs: vec![file1.clone(), File::new("out.h")], - attrs: vec![], ..Default::default() }), );
diff --git a/src/gn/starlark/crates/ffi/src/bridge.rs b/src/gn/starlark/crates/ffi/src/bridge.rs index 96f55d4..dd32aee 100644 --- a/src/gn/starlark/crates/ffi/src/bridge.rs +++ b/src/gn/starlark/crates/ffi/src/bridge.rs
@@ -84,6 +84,7 @@ ) -> SliceAny; // Returns an OwnedSlice<KeyValue> corresponding to references to each element. pub(in crate::scope) fn GetScopeItems(scope: &Scope) -> SliceAny; + pub(in crate::scope) fn GetValue(scope: &Scope, ident: &str) -> *const Value; #[rust_name = "settings_cxx"] pub(in crate::scope) fn settings(self: &Scope) -> *const Settings;
diff --git a/src/gn/starlark/crates/ffi/src/lib.rs b/src/gn/starlark/crates/ffi/src/lib.rs index 2e698a0..988080a 100644 --- a/src/gn/starlark/crates/ffi/src/lib.rs +++ b/src/gn/starlark/crates/ffi/src/lib.rs
@@ -30,5 +30,6 @@ pub use bridge::{KeyValue, Label, OutputFile, Scope, Settings, SourceDir, Value, ValueType}; pub use mutability::Immutable; pub use opaque::{NonOpaque, OpaqueSized}; +pub use scope::OwnedScope; pub use slice::{OwnedSlice, Slice}; pub use test_with_scope::TestWithScope;
diff --git a/src/gn/starlark/crates/ffi/src/scope.rs b/src/gn/starlark/crates/ffi/src/scope.rs index e063d7a..54a43a7 100644 --- a/src/gn/starlark/crates/ffi/src/scope.rs +++ b/src/gn/starlark/crates/ffi/src/scope.rs
@@ -4,7 +4,7 @@ use std::pin::Pin; -use starlark::values::FrozenValue; +use starlark::values::{Heap, Value as StarlarkValue}; use crate::{bridge::Value, Immutable, OwnedSlice, Scope}; @@ -31,16 +31,74 @@ } /// Converts Scope items to Starlark key-value pairs. - pub fn get_kv<'a>( - &'a self, - frozen_heap: &starlark::values::FrozenHeap, - ) -> Vec<(&'a str, FrozenValue)> { + pub fn get_kv<'v>(&self, heap: &Heap<'v>) -> Vec<(&str, StarlarkValue<'v>)> { let owned = self.items(); let mut items = Vec::new(); - // Iterate over the KeyValue contiguous slice: for pair in owned.as_slice() { - items.push((pair.key, pair.value.to_rust(frozen_heap))); + items.push((pair.key, pair.value.to_rust(heap))); } items } } + +pub struct OwnedScope(pub cxx::UniquePtr<Scope>); + +impl types::Scope for OwnedScope { + fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, StarlarkValue<'v>)>) -> Self { + let parent = self.0.as_ref().unwrap(); + let (keys, vals): (Vec<&str>, Vec<StarlarkValue<'v>>) = kv.unzip(); + let (mut child_scope, mut placeholders) = Scope::new(parent, &keys); + + // Safety: The child scope will always be non-null. + let child_ref = unsafe { child_scope.as_mut().unwrap().get_unchecked_mut() }; + for (placeholder, val) in placeholders.as_slice_mut().iter_mut().zip(vals) { + placeholder + .as_mut() + .assign(val, child_ref, std::ptr::null()); + } + + Self(child_scope) + } + + fn get<'v>(&self, key: &str, heap: &Heap<'v>) -> Option<StarlarkValue<'v>> { + let parent = self.0.as_ref().unwrap(); + let val_ptr = crate::bridge::GetValue(parent, key); + // Safety: val_ptr is either null or a valid pointer backed by the parent scope + // lifetime. + unsafe { val_ptr.as_ref() }.map(|val| val.to_rust(heap)) + } +} + +#[cfg(test)] +mod tests { + use types::Scope as _; + + use super::*; + use crate::TestWithScope; + + #[test] + fn test_owned_scope_methods() { + let mut setup = TestWithScope::new(); + let parent_scope = setup.scope(); + + let (child_ptr, _) = Scope::new(parent_scope, &[]); + let owned_scope = OwnedScope(child_ptr); + + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + + let val_int = heap.alloc(42); + let val_str = heap.alloc("hello"); + + let grandchild = + owned_scope.copy_with(vec![("foo", val_int), ("bar", val_str)].into_iter()); + + assert_eq!(grandchild.get("foo", &heap).unwrap().unpack_i32(), Some(42)); + assert_eq!( + grandchild.get("bar", &heap).unwrap().unpack_str(), + Some("hello") + ); + assert!(grandchild.get("baz", &heap).is_none()); + }); + } +}
diff --git a/src/gn/starlark/crates/ffi/src/value.rs b/src/gn/starlark/crates/ffi/src/value.rs index 95a26c0..39bd977 100644 --- a/src/gn/starlark/crates/ffi/src/value.rs +++ b/src/gn/starlark/crates/ffi/src/value.rs
@@ -4,7 +4,7 @@ use std::pin::Pin; -use starlark::values::{list::ListRef, structs::StructRef, FrozenValue}; +use starlark::values::{list::ListRef, structs::StructRef}; pub use crate::bridge::ParseNode; use crate::{ @@ -17,25 +17,23 @@ Immutable::from(Slice::from(crate::bridge::list_value_cxx(self))) } - pub fn to_rust(&self, frozen_heap: &starlark::values::FrozenHeap) -> FrozenValue { + pub fn to_rust<'v>(&self, heap: &starlark::values::Heap<'v>) -> starlark::values::Value<'v> { match self.kind() { - ValueType::None => FrozenValue::new_none(), - ValueType::Boolean => FrozenValue::new_bool(*self.boolean_value()), - ValueType::Integer => frozen_heap.alloc(*self.int_value()), - ValueType::String => frozen_heap.alloc(self.string_value()), + ValueType::None => starlark::values::Value::new_none(), + ValueType::Boolean => starlark::values::Value::new_bool(*self.boolean_value()), + ValueType::Integer => heap.alloc(*self.int_value()), + ValueType::String => heap.alloc(self.string_value()), ValueType::List => { let slice = self.list_value(); - let items: Vec<_> = slice.iter().map(|item| item.to_rust(frozen_heap)).collect(); - frozen_heap.alloc(items) + let items: Vec<_> = slice.iter().map(|item| item.to_rust(heap)).collect(); + heap.alloc(items) }, ValueType::Scope => { let scope_ptr = self.scope_value(); // Safety: C++ Value invariants guarantee that scope_value() is never null // when the type is SCOPE. let scope = unsafe { &*scope_ptr }; - frozen_heap.alloc(starlark::values::structs::AllocStruct( - scope.get_kv(frozen_heap), - )) + heap.alloc(starlark::values::structs::AllocStruct(scope.get_kv(heap))) }, _ => unreachable!(), } @@ -97,13 +95,13 @@ #[cfg(test)] mod tests { - use starlark::values::FrozenHeap; + use starlark::values::{FrozenValue, Heap, ValueLike as _}; use super::*; use crate::TestWithScope; fn back_and_forth<'v>( - heap: &FrozenHeap, + heap: &Heap<'v>, val: starlark::values::Value<'v>, ) -> starlark::values::Value<'v> { let mut setup = TestWithScope::new(); @@ -111,89 +109,101 @@ let mut value = crate::bridge::NewValueForTesting(); value.pin_mut().assign(val, scope, std::ptr::null()); - value.to_rust(heap).to_value() + value.to_rust(heap) } #[test] fn test_none_conversion() { - let heap = FrozenHeap::new(); - assert!(back_and_forth(&heap, FrozenValue::new_none().to_value()).is_none()); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + assert!(back_and_forth(&heap, FrozenValue::new_none().to_value()).is_none()); + }); } #[test] fn test_bool_conversion() { - let heap = FrozenHeap::new(); - assert_eq!( - back_and_forth(&heap, heap.alloc(true).to_value()).unpack_bool(), - Some(true) - ); - assert_eq!( - back_and_forth(&heap, heap.alloc(false).to_value()).unpack_bool(), - Some(false) - ); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + assert_eq!( + back_and_forth(&heap, heap.alloc(true).to_value()).unpack_bool(), + Some(true) + ); + assert_eq!( + back_and_forth(&heap, heap.alloc(false).to_value()).unpack_bool(), + Some(false) + ); + }); } #[test] fn test_int_conversion() { - let heap = FrozenHeap::new(); - assert_eq!( - back_and_forth(&heap, heap.alloc(123456789i32).to_value()).unpack_i32(), - Some(123456789) - ); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + assert_eq!( + back_and_forth(&heap, heap.alloc(123456789i32).to_value()).unpack_i32(), + Some(123456789) + ); + }); } #[test] fn test_string_conversion() { - let heap = FrozenHeap::new(); - assert_eq!( - back_and_forth(&heap, heap.alloc("hello world").to_value()).unpack_str(), - Some("hello world") - ); - assert_eq!( - back_and_forth( - &heap, - heap.alloc("hello long string without SSO optimizations") - .to_value(), - ) - .unpack_str(), - Some("hello long string without SSO optimizations") - ); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + assert_eq!( + back_and_forth(&heap, heap.alloc("hello world").to_value()).unpack_str(), + Some("hello world") + ); + assert_eq!( + back_and_forth( + &heap, + heap.alloc("hello long string without SSO optimizations") + .to_value(), + ) + .unpack_str(), + Some("hello long string without SSO optimizations") + ); + }); } #[test] fn test_list_conversion() { - let heap = FrozenHeap::new(); - let list_ref = ListRef::from_value(back_and_forth( - &heap, - heap.alloc(vec![heap.alloc(42), heap.alloc("hello")]) - .to_value(), - )) - .unwrap(); - assert_eq!(list_ref.len(), 2); - let mut iter = list_ref.iter(); - assert_eq!(iter.next().unwrap().unpack_i32(), Some(42)); - assert_eq!(iter.next().unwrap().unpack_str(), Some("hello")); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + let list_ref = ListRef::from_value(back_and_forth( + &heap, + heap.alloc(vec![heap.alloc(42), heap.alloc("hello")]) + .to_value(), + )) + .unwrap(); + assert_eq!(list_ref.len(), 2); + let mut iter = list_ref.iter(); + assert_eq!(iter.next().unwrap().unpack_i32(), Some(42)); + assert_eq!(iter.next().unwrap().unpack_str(), Some("hello")); + }); } #[test] fn test_struct_conversion() { - let heap = FrozenHeap::new(); - let struct_ref = StructRef::from_value(back_and_forth( - &heap, - heap.alloc(starlark::values::structs::AllocStruct(vec![ - ("foo", heap.alloc(100)), - ("bar", heap.alloc("baz")), - ])) - .to_value(), - )) - .unwrap(); - let get_field = |name: &str| { - struct_ref - .iter() - .find(|(k, _)| k.as_str() == name) - .map(|(_, v)| v) - }; - assert_eq!(get_field("foo").unwrap().unpack_i32(), Some(100)); - assert_eq!(get_field("bar").unwrap().unpack_str(), Some("baz")); + starlark::environment::Module::with_temp_heap(|module| { + let heap = module.heap(); + let struct_ref = StructRef::from_value(back_and_forth( + &heap, + heap.alloc(starlark::values::structs::AllocStruct(vec![ + ("foo", heap.alloc(100)), + ("bar", heap.alloc("baz")), + ])) + .to_value(), + )) + .unwrap(); + let get_field = |name: &str| { + struct_ref + .iter() + .find(|(k, _)| k.as_str() == name) + .map(|(_, v)| v) + }; + assert_eq!(get_field("foo").unwrap().unpack_i32(), Some(100)); + assert_eq!(get_field("bar").unwrap().unpack_str(), Some("baz")); + }); } }
diff --git a/src/gn/starlark/crates/testutils/src/eval_context.rs b/src/gn/starlark/crates/testutils/src/eval_context.rs index b5707e1..36c721c 100644 --- a/src/gn/starlark/crates/testutils/src/eval_context.rs +++ b/src/gn/starlark/crates/testutils/src/eval_context.rs
@@ -1,16 +1,43 @@ // Copyright 2026 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. +use std::collections::HashMap; use attr::{Attr, EvalContext as AttrEvalContext, EvalContextAttrExt, Session as AttrSession}; use starlark::{ - values::{FrozenValue, ProvidesStaticType}, + values::{FrozenValue, Heap, ProvidesStaticType, Value}, Result, }; -use types::{CtxState, Label, LabelRef, Package, PackageRef, PathResolver, Session}; +use types::{ + CtxState, Label, LabelRef, OutputType, Package, PackageRef, PathResolver, Scope, Session, +}; use crate::{FakeSession, FakeTarget, FakeTargetRef}; +#[derive(Clone, Default, Debug)] +pub struct FakeScope(HashMap<String, Value<'static>>); + +impl Scope for FakeScope { + fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, Value<'v>)>) -> Self { + let mut values = self.0.clone(); + for (k, v) in kv { + // Safety: Transmuting 'v to 'static is safe because this mock scope + // is only used in tests, where the Starlark heap outlives the evaluation + // context. + let static_val = unsafe { std::mem::transmute::<Value<'v>, Value<'static>>(v) }; + values.insert(k.to_owned(), static_val); + } + Self(values) + } + + fn get<'v>(&self, key: &str, _heap: &Heap<'v>) -> Option<Value<'v>> { + self.0.get(key).map(|v| { + // Safety: Shortening the lifetime is always safe. + unsafe { std::mem::transmute::<Value<'static>, Value<'v>>(*v) } + }) + } +} + /// A simple implementation of the evaluation context used in Starlark unit /// tests. #[derive(allocative::Allocative)] @@ -28,6 +55,9 @@ /// The fake rule state. #[allocative(skip)] pub rule_state: CtxState<FakeTargetRef>, + /// The fake scope. + #[allocative(skip)] + pub scope: FakeScope, } unsafe impl<'v> ProvidesStaticType<'v> for FakeEvalContext { @@ -50,12 +80,14 @@ session, path_resolver: PathResolver::new_for_testing(), rule_state: CtxState::new(FakeTargetRef::default()), + scope: FakeScope::default(), } } } impl AttrEvalContext for FakeEvalContext { type Session = FakeSession; + type Scope = FakeScope; fn session(&self) -> &Self::Session { &self.session @@ -73,8 +105,8 @@ self.current_toolchain.as_ref() } - fn require_macro(&self) -> Result<()> { - Ok(()) + fn require_macro(&self) -> Result<&FakeScope> { + Ok(&self.scope) } fn require_bzl(&self) -> Result<()> { @@ -93,14 +125,19 @@ } impl EvalContextAttrExt for FakeEvalContext { - fn create_starlark_target( + fn create_target( &self, + target_type: Option<OutputType>, target_name: &str, - _rule: FrozenValue, + scope: &FakeScope, + rule: FrozenValue, attrs: Vec<Attr>, ) -> Result<<Self::Session as AttrSession>::TargetRef> { let label = Label::new(self.package.clone(), target_name.to_owned()); let target = FakeTargetRef::new(FakeTarget { + output_type: target_type, + rule, + cxx_attrs: scope.0.clone(), outputs: vec![], attrs, ..Default::default()
diff --git a/src/gn/starlark/crates/testutils/src/target.rs b/src/gn/starlark/crates/testutils/src/target.rs index d0be24c..da1fa2c 100644 --- a/src/gn/starlark/crates/testutils/src/target.rs +++ b/src/gn/starlark/crates/testutils/src/target.rs
@@ -3,7 +3,7 @@ // found in the LICENSE file. use std::{ - collections::HashSet, + collections::{HashMap, HashSet}, hash::Hasher, sync::{Arc, Mutex}, }; @@ -12,10 +12,10 @@ use attr::Attr; use starlark::{ starlark_simple_value, - values::{ProvidesStaticType, StarlarkValue, Value, ValueLike}, + values::{FrozenValue, ProvidesStaticType, StarlarkValue, Value, ValueLike}, }; use starlark_derive::{starlark_value, NoSerialize}; -use types::{File, IPromiseToImplementStarlarkEqAndHash, Label, TargetRef}; +use types::{File, IPromiseToImplementStarlarkEqAndHash, Label, OutputType, TargetRef}; /// A fake target struct for testing. #[derive(Debug, Allocative, Default)] @@ -24,11 +24,33 @@ pub outputs: Vec<File>, /// A list of attributes. pub attrs: Vec<Attr>, + pub output_type: Option<OutputType>, + pub rule: FrozenValue, + #[allocative(skip)] + pub cxx_attrs: HashMap<String, Value<'static>>, /// Registered target dependencies. #[allocative(skip)] pub dependencies: Mutex<HashSet<(Label, Label)>>, } +impl PartialEq for FakeTarget { + fn eq(&self, other: &Self) -> bool { + self.outputs == other.outputs + && self.attrs == other.attrs + && self.output_type == other.output_type + && self.rule == other.rule + && self.cxx_attrs.len() == other.cxx_attrs.len() + && self.cxx_attrs.iter().all(|(k, v)| { + other.cxx_attrs.get(k).map_or(false, |ov| { + v.equals(*ov).unwrap_or(false) + }) + }) + && *self.dependencies.lock().unwrap() == *other.dependencies.lock().unwrap() + } +} +impl Eq for FakeTarget {} + + /// A reference to a fake target. #[derive(Debug, ProvidesStaticType, NoSerialize, Allocative, Clone)] pub struct FakeTargetRef(#[allocative(skip)] Arc<FakeTarget>);
diff --git a/src/gn/starlark/crates/types/Cargo.toml b/src/gn/starlark/crates/types/Cargo.toml index ee19c49..120aacb 100644 --- a/src/gn/starlark/crates/types/Cargo.toml +++ b/src/gn/starlark/crates/types/Cargo.toml
@@ -10,8 +10,9 @@ doctest = false [dependencies] +allocative = { workspace = true } +cxx = { workspace = true } starlark = { workspace = true } starlark_derive = { workspace = true } +strum = { workspace = true } thiserror = { workspace = true } -cxx = { workspace = true } -allocative = { workspace = true }
diff --git a/src/gn/starlark/crates/types/src/eval_context.rs b/src/gn/starlark/crates/types/src/eval_context.rs index f23ae6c..73b8f87 100644 --- a/src/gn/starlark/crates/types/src/eval_context.rs +++ b/src/gn/starlark/crates/types/src/eval_context.rs
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use crate::{LabelRef, PackageRef, PathResolver, Session}; +use crate::{LabelRef, PackageRef, PathResolver, Scope, Session}; /// The starlark Evaluator has an "extra" object that you can use to store /// whatever metadata you want, accessible to any custom functions you write @@ -20,6 +20,9 @@ /// The session type associated with this context. type Session: Session; + /// The scope type associated with this context. + type Scope: Scope; + /// Returns the package currently being evaluated. fn current_package(&self) -> &PackageRef; @@ -34,8 +37,9 @@ /// Asserts that the current evaluation context is a macro context. /// Use when a function cannot be called from other contexts (eg. - /// `static_library` cannot be called inside rule evaluation) - fn require_macro(&self) -> starlark::Result<()>; + /// `static_library` cannot be called inside rule evaluation). + /// Returns the scope of the place the macro was called from. + fn require_macro(&self) -> starlark::Result<&Self::Scope>; /// Asserts that the current evaluation context is a bzl file context. fn require_bzl(&self) -> starlark::Result<()>;
diff --git a/src/gn/starlark/crates/types/src/lib.rs b/src/gn/starlark/crates/types/src/lib.rs index 5a9d617..33c7fd3 100644 --- a/src/gn/starlark/crates/types/src/lib.rs +++ b/src/gn/starlark/crates/types/src/lib.rs
@@ -11,6 +11,8 @@ pub mod package; pub mod package_ref; pub mod path_resolver; +pub mod output_type; +pub mod scope; pub mod session; pub mod target_ref; pub mod unpacked_owned_value; @@ -22,9 +24,11 @@ pub use file::File; pub use label::Label; pub use label_ref::LabelRef; +pub use output_type::OutputType; pub use package::Package; pub use package_ref::PackageRef; pub use path_resolver::PathResolver; +pub use scope::Scope; pub use session::Session; pub use target_ref::{IPromiseToImplementStarlarkEqAndHash, TargetRef}; pub use unpacked_owned_value::UnpackedOwnedValue;
diff --git a/src/gn/starlark/crates/types/src/output_type.rs b/src/gn/starlark/crates/types/src/output_type.rs new file mode 100644 index 0000000..d3af62c --- /dev/null +++ b/src/gn/starlark/crates/types/src/output_type.rs
@@ -0,0 +1,28 @@ +// Copyright 2026 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. + +use allocative::Allocative; +use strum::{Display, EnumIter, IntoStaticStr}; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter, Display, IntoStaticStr, Allocative)] +#[strum(serialize_all = "snake_case")] +/// Copied from target.h's OutputType enum. +pub enum OutputType { + // Unknown = 0 + Group = 1, + Executable, + SharedLibrary, + LoadableModule, + StaticLibrary, + SourceSet, + Copy, + Action, + ActionForEach, + BundleData, + CreateBundle, + GeneratedFile, + RustLibrary, + RustProcMacro, +}
diff --git a/src/gn/starlark/crates/types/src/scope.rs b/src/gn/starlark/crates/types/src/scope.rs new file mode 100644 index 0000000..870d93a --- /dev/null +++ b/src/gn/starlark/crates/types/src/scope.rs
@@ -0,0 +1,19 @@ +// Copyright 2026 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. + +use starlark::values::{Heap, Value}; + +/// This trait is an abstraction over GN's Scope object. +/// +/// We intentionally expose, rather than methods that C++ scope objects support, +/// the API we actually wish to use from rust, which may be an abstraction +/// over that. +pub trait Scope { + /// Creates a copy of the scope with some additional values set. + fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, Value<'v>)>) -> Self; + + /// Retrieves a value from the key-value store. + /// May allocate the value it retrieves on the heap. + fn get<'v>(&self, key: &str, heap: &Heap<'v>) -> Option<Value<'v>>; +}