1- # AWS Greengrass Classic Component Class
1+ # AWS Greengrass Classic Component Class (V2 Java / bin)
22#
33# This bbclass supports Greengrass Classic (greengrass-bin) components.
44# For Greengrass Lite components, use greengrass-lite-component.bbclass instead.
3232# COMPONENT_ARTIFACTS = "hello_world.py"
3333# inherit greengrass-component
3434
35- require greengrass -component -plugin -common . inc
35+ require greengrass -common . inc
36+
37+ # Extract component information from YAML file, allow recipe override
38+ python __anonymous () {
39+ import yaml
40+ import os
41+
42+ # First, check if COMPONENT_NAME and COMPONENT_VERSION are already set in recipe
43+ component_name = d . getVar ('COMPONENT_NAME' )
44+ component_version = d . getVar ('COMPONENT_VERSION' )
45+
46+ # Check if component-recipe.yaml exists in SRC_URI
47+ src_uri = d . getVar ('SRC_URI' ) or ''
48+ has_component_recipe = 'component-recipe.yaml' in src_uri
49+
50+ if has_component_recipe and (not component_name or not component_version ):
51+ # Try to find the YAML file in the recipe directory
52+ file_dirname = d . getVar ('FILE_DIRNAME' )
53+ pn = d . getVar ('PN' )
54+
55+ # Look for component-recipe.yaml in the recipe's files directory
56+ possible_paths = [
57+ os . path . join (file_dirname , pn , 'component-recipe.yaml' ),
58+ os . path . join (file_dirname , 'files' , 'component-recipe.yaml' ),
59+ os . path . join (file_dirname , pn . replace ('greengrass-component-' , '' ), 'component-recipe.yaml' )
60+ ]
61+
62+ yaml_file = None
63+ for path in possible_paths :
64+ if os . path . exists (path ):
65+ yaml_file = path
66+ break
67+
68+ if yaml_file :
69+ try :
70+ with open (yaml_file , 'r' ) as f :
71+ recipe = yaml . safe_load (f )
72+
73+ # Set COMPONENT_NAME from YAML if not already set in recipe
74+ if not component_name :
75+ yaml_component_name = recipe . get ('ComponentName' )
76+ if yaml_component_name :
77+ d . setVar ('COMPONENT_NAME' , yaml_component_name )
78+ bb . note (f "Set COMPONENT_NAME from YAML: {yaml_component_name}" )
79+ component_name = yaml_component_name
80+
81+ # Set COMPONENT_VERSION from YAML if not already set in recipe
82+ if not component_version :
83+ yaml_component_version = recipe . get ('ComponentVersion' , '1.0.0' )
84+ d . setVar ('COMPONENT_VERSION' , yaml_component_version )
85+ bb . note (f "Set COMPONENT_VERSION from YAML: {yaml_component_version}" )
86+ component_version = yaml_component_version
87+
88+ except Exception as e :
89+ bb . warn (f "Could not read component info from YAML {yaml_file}: {e}" )
90+
91+ # Ensure defaults are set if still not defined
92+ if not component_name :
93+ bb . error ("COMPONENT_NAME must be set either in recipe or component-recipe.yaml" )
94+
95+ if not component_version :
96+ d . setVar ('COMPONENT_VERSION' , '1.0.0' )
97+ }
3698
3799inherit deploy
38100
@@ -47,11 +109,11 @@ GREENGRASS_VARIANT ?= "classic"
47109# Validate variant and warn about Lite
48110python __anonymous () {
49111 variant = d . getVar ('GREENGRASS_VARIANT' )
50-
112+
51113 # Validate variant value
52114 if variant not in ['classic' , 'lite' ]:
53115 bb . fatal (f "Invalid GREENGRASS_VARIANT '{variant}'. Must be 'classic' or 'lite'" )
54-
116+
55117 if variant == 'lite' :
56118 bb . warn ("GREENGRASS_VARIANT='lite' detected. Consider using 'greengrass-lite-component' class for better Lite support and clearer image-provided component paths." )
57119 # Set basic Lite paths for backward compatibility
@@ -62,47 +124,47 @@ python __anonymous() {
62124 d . setVar ('GG_COMPONENT_ROOT' , '/${GG_BASENAME} /components' )
63125 d . setVar ('GG_CONFIG_DIR' , '/${GG_BASENAME} /config' )
64126 d . setVar ('GG_CONFIG_FRAGMENT_DIR' , 'greengrass-plugin-fragments' )
65-
127+
66128 bb . note (f "Greengrass variant configured: {variant}" )
67129}
68130
69131# Python function to convert component recipe to config fragment
70132python convert_recipe_to_fragment () {
71133 import yaml
72134 import os
73-
135+
74136 recipe_file = d . getVar ('recipe_file' )
75137 output_file = d . getVar ('output_file' )
76138 variant = d . getVar ('GREENGRASS_VARIANT' )
77-
139+
78140 if not recipe_file or not os . path . exists (recipe_file ):
79141 bb . error (f "Recipe file not found: {recipe_file}" )
80142 d . setVar ('CONVERSION_SUCCESS' , '0' )
81143 return
82-
144+
83145 if not output_file :
84146 bb . error ("Output file not specified" )
85147 d . setVar ('CONVERSION_SUCCESS' , '0' )
86148 return
87-
149+
88150 try :
89151 with open (recipe_file , 'r' ) as f :
90152 recipe = yaml . safe_load (f )
91153 except Exception as e :
92154 bb . error (f "Error reading recipe file {recipe_file}: {e}" )
93155 d . setVar ('CONVERSION_SUCCESS' , '0' )
94156 return
95-
157+
96158 # Extract key information from the recipe
97159 component_name = recipe . get ('ComponentName' )
98160 component_version = recipe . get ('ComponentVersion' , '1.0.0' )
99161 component_type = recipe . get ('ComponentType' , 'aws.greengrass.generic' )
100-
162+
101163 if not component_name :
102164 bb . error ("ComponentName not found in recipe" )
103165 d . setVar ('CONVERSION_SUCCESS' , '0' )
104166 return
105-
167+
106168 # Build the config fragment based on variant
107169 if variant == 'lite' :
108170 # Greengrass Lite uses simpler YAML structure
@@ -114,12 +176,12 @@ python convert_recipe_to_fragment() {
114176 }
115177 }
116178 }
117-
179+
118180 # Add configuration if present
119181 config = recipe . get ('ComponentConfiguration' , {}). get ('DefaultConfiguration' , {})
120182 if config :
121183 fragment ['services' ][component_name ]['configuration' ] = config
122-
184+
123185 # Process manifests for lifecycle (simplified for lite)
124186 manifests = recipe . get ('Manifests' , [])
125187 if manifests :
@@ -137,34 +199,42 @@ python convert_recipe_to_fragment() {
137199 }
138200 }
139201 }
140-
202+
141203 # Add configuration if present
142204 config = recipe . get ('ComponentConfiguration' , {}). get ('DefaultConfiguration' , {})
143205 if config :
144206 fragment ['services' ][component_name ]['configuration' ] = config
145-
207+
146208 # Process manifests to extract lifecycle and artifacts
147209 manifests = recipe . get ('Manifests' , [])
148210 if manifests :
149211 # Use the first manifest (typically Linux)
150212 manifest = manifests [0 ]
151-
213+
152214 # Add lifecycle information
153215 lifecycle = manifest . get ('Lifecycle' , {})
154216 if lifecycle :
155- fragment ['services' ][component_name ]['Lifecycle' ] = lifecycle
156-
217+ # Replace {artifacts:path} placeholder with actual component path
218+ component_path = f "/greengrass/v2/components/{component_name}/{component_version}"
219+ processed_lifecycle = {}
220+ for key , value in lifecycle . items ():
221+ if isinstance (value , str ):
222+ processed_lifecycle [key ] = value . replace ('{artifacts:path}' , component_path )
223+ else :
224+ processed_lifecycle [key ] = value
225+ fragment ['services' ][component_name ]['Lifecycle' ] = processed_lifecycle
226+
157227 # Add artifacts information
158228 artifacts = manifest . get ('Artifacts' , [])
159229 if artifacts :
160230 fragment ['services' ][component_name ]['Artifacts' ] = artifacts
161-
231+
162232 # Add dependencies structure for bin variant
163233 fragment ['services' ]['main' ] = {
164234 'dependencies' : [component_name ],
165235 'lifecycle' : {}
166236 }
167-
237+
168238 # Write the fragment
169239 try :
170240 with open (output_file , 'w' ) as f :
@@ -221,14 +291,14 @@ python do_deploy() {
221291 import shutil
222292 import tempfile
223293 import subprocess
224-
294+
225295 variant = d . getVar ('GREENGRASS_VARIANT' )
226296 deploydir = d . getVar ('DEPLOYDIR' )
227297 component_name = d . getVar ('COMPONENT_NAME' )
228-
298+
229299 # Ensure DEPLOYDIR exists for all variants to satisfy sstate requirements
230300 os . makedirs (deploydir , exist_ok = True )
231-
301+
232302 # Skip actual deploy logic for lite components - everything is handled in do_install
233303 if variant == 'lite' :
234304 bb . note ("Skipping deploy for greengrass-lite component - config handled in do_install" )
@@ -237,22 +307,22 @@ python do_deploy() {
237307 with open (marker_file , 'w' ) as f :
238308 f . write ("Greengrass Lite component deployed via do_install\n" )
239309 return
240-
310+
241311 # Only handle greengrass-bin components in deploy
242312 unpackdir = d . getVar ('S' ) # Use S instead of UNPACKDIR for source files
243-
313+
244314 # For Greengrass Bin, use deployment directory for fragment merging
245315 fragment_dir = os . path . join (deploydir , d . getVar ('GG_CONFIG_FRAGMENT_DIR' ))
246316 os . makedirs (fragment_dir , exist_ok = True )
247317 fragment_file = os . path . join (fragment_dir , f "{component_name}.yaml" )
248-
318+
249319 # Priority order for configuration files
250320 config_files = [
251321 'config.yaml.template' , # Direct fragment (highest priority)
252322 'component-recipe.yaml' , # Standard recipe (convert)
253323 'component-config.yaml.template' # Legacy format (convert)
254324 ]
255-
325+
256326 for config_file in config_files :
257327 config_path = os . path . join (unpackdir , config_file )
258328 if os . path . exists (config_path ):
@@ -264,11 +334,11 @@ python do_deploy() {
264334 # Convert recipe to fragment using inline function
265335 d . setVar ('recipe_file' , config_path )
266336 d . setVar ('output_file' , fragment_file )
267-
337+
268338 # Call conversion function and check success flag
269339 bb . build . exec_func ('convert_recipe_to_fragment' , d )
270340 success = d . getVar ('CONVERSION_SUCCESS' )
271-
341+
272342 if success == '1' :
273343 bb . note (f "Converted {config_file} to config fragment for greengrass-bin" )
274344 else :
@@ -297,7 +367,7 @@ addtask deploy after do_install before do_populate_sysroot
297367python __anonymous () {
298368 variant = d . getVar ('GREENGRASS_VARIANT' )
299369 pn = d . getVar ('PN' )
300-
370+
301371 if variant == 'lite' :
302372 # Greengrass Lite files
303373 files = d . getVar ('FILES:' + pn ) or ''
@@ -309,3 +379,11 @@ python __anonymous() {
309379 files += ' /${GG_BASENAME} /components/'
310380 d . setVar ('FILES:' + pn , files )
311381}
382+
383+ # Ensure all Greengrass plugins/components wait for greengrass-bin to install base structure
384+ python __anonymous () {
385+ pn = d . getVar ('PN' )
386+ if pn and pn != 'greengrass-bin*' :
387+ # Add dependency on greengrass-bin's do_install task
388+ d . appendVarFlag ('do_install' , 'depends' , ' greengrass-bin:do_install' )
389+ }
0 commit comments