1#!/usr/bin/env python3
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Generate Toaster Fixtures for 'poky.xml' and 'oe-core.xml'
6#
7# Copyright (C) 2022      Wind River Systems
8# SPDX-License-Identifier: GPL-2.0-only
9#
10# Edit the 'current_releases' table for each new release cycle
11#
12# Usage: ./get_fixtures all
13#
14
15import os
16import sys
17import argparse
18
19verbose = False
20
21####################################
22# Releases
23#
24# https://wiki.yoctoproject.org/wiki/Releases
25#
26# NOTE: for the current releases table, it helps to keep continuing releases
27# in the same table positions since this minimizes the patch diff for review.
28# The order of the table does not matter since Toaster presents them sorted.
29#
30# Traditionally, the two most current releases are included in addition to the
31# 'master' branch and the local installation's 'HEAD'.
32# It is also policy to include all active LTS releases.
33#
34
35# [Codename, Yocto Project Version, Release Date, Current Version, Support Level, Poky Version, BitBake branch]
36current_releases = [
37    # Release slot #1
38    ['Kirkstone','4.0','April 2022','4.0.8 (March 2023)','Stable - Long Term Support (until Apr. 2024)','','2.0'],
39    # Release slot #2 'local'
40    ['HEAD','HEAD','','Local Yocto Project','HEAD','','HEAD'],
41    # Release slot #3 'master'
42    ['Master','master','','Yocto Project master','master','','master'],
43    # Release slot #4
44    ['Mickledore','4.2','April 2023','4.2.0 (April 2023)','Support for 7 months (until October 2023)','','2.4'],
45#   ['Langdale','4.1','October 2022','4.1.2 (January 2023)','Support for 7 months (until May 2023)','','2.2'],
46#   ['Honister','3.4','October 2021','3.4.2 (February 2022)','Support for 7 months (until May 2022)','26.0','1.52'],
47#   ['Hardknott','3.3','April 2021','3.3.5 (March 2022)','Stable - Support for 13 months (until Apr. 2022)','25.0','1.50'],
48#   ['Gatesgarth','3.2','Oct 2020','3.2.4 (May 2021)','EOL','24.0','1.48'],
49    # Optional Release slot #5
50    ['Dunfell','3.1','April 2020','3.1.23 (February 2023)','Stable - Long Term Support (until Apr. 2024)','23.0','1.46'],
51]
52
53default_poky_layers = [
54    'openembedded-core',
55    'meta-poky',
56    'meta-yocto-bsp',
57]
58
59default_oe_core_layers = [
60    'openembedded-core',
61]
62
63####################################
64# Templates
65
66prolog_template = '''\
67<?xml version="1.0" encoding="utf-8"?>
68<django-objects version="1.0">
69  <!-- Set the project default value for DISTRO -->
70  <object model="orm.toastersetting" pk="1">
71    <field type="CharField" name="name">DEFCONF_DISTRO</field>
72    <field type="CharField" name="value">{{distro}}</field>
73  </object>
74'''
75
76#<!-- Bitbake versions which correspond to the metadata release -->')
77bitbakeversion_poky_template = '''\
78  <object model="orm.bitbakeversion" pk="{{bitbake_id}}">
79    <field type="CharField" name="name">{{name}}</field>
80    <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field>
81    <field type="CharField" name="branch">{{branch}}</field>
82    <field type="CharField" name="dirpath">bitbake</field>
83  </object>
84'''
85bitbakeversion_oecore_template = '''\
86  <object model="orm.bitbakeversion" pk="{{bitbake_id}}">
87    <field type="CharField" name="name">{{name}}</field>
88    <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field>
89    <field type="CharField" name="branch">{{bitbakeversion}}</field>
90  </object>
91'''
92
93# <!-- Releases available -->
94releases_available_template = '''\
95  <object model="orm.release" pk="{{ra_count}}">
96    <field type="CharField" name="name">{{name}}</field>
97    <field type="CharField" name="description">{{description}}</field>
98    <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">{{ra_count}}</field>
99    <field type="CharField" name="branch_name">{{release}}</field>
100    <field type="TextField" name="helptext">Toaster will run your builds {{help_source}}.</field>
101  </object>
102'''
103
104# <!-- Default project layers for each release -->
105default_layers_template = '''\
106  <object model="orm.releasedefaultlayer" pk="{{rdl_count}}">
107    <field rel="ManyToOneRel" to="orm.release" name="release">{{release_id}}</field>
108    <field type="CharField" name="layer_name">{{layer}}</field>
109  </object>
110'''
111
112default_layers_preface = '''\
113  <!-- Default layers provided by poky
114       openembedded-core
115       meta-poky
116       meta-yocto-bsp
117  -->
118'''
119
120layer_poky_template = '''\
121  <object model="orm.layer" pk="{{layer_id}}">
122    <field type="CharField" name="name">{{layer}}</field>
123    <field type="CharField" name="layer_index_url"></field>
124    <field type="CharField" name="vcs_url">{{vcs_url}}</field>
125    <field type="CharField" name="vcs_web_url">{{vcs_web_url}}</field>
126    <field type="CharField" name="vcs_web_tree_base_url">{{vcs_web_tree_base_url}}</field>
127    <field type="CharField" name="vcs_web_file_base_url">{{vcs_web_file_base_url}}</field>
128  </object>
129'''
130
131layer_oe_core_template = '''\
132  <object model="orm.layer" pk="{{layer_id}}">
133    <field type="CharField" name="name">{{layer}}</field>
134    <field type="CharField" name="vcs_url">{{vcs_url}}</field>
135    <field type="CharField" name="vcs_web_url">{{vcs_web_url}}</field>
136    <field type="CharField" name="vcs_web_tree_base_url">{{vcs_web_tree_base_url}}</field>
137    <field type="CharField" name="vcs_web_file_base_url">{{vcs_web_file_base_url}}</field>
138  </object>
139'''
140
141layer_version_template = '''\
142  <object model="orm.layer_version" pk="{{lv_count}}">
143    <field rel="ManyToOneRel" to="orm.layer" name="layer">{{layer_id}}</field>
144    <field type="IntegerField" name="layer_source">0</field>
145    <field rel="ManyToOneRel" to="orm.release" name="release">{{release_id}}</field>
146    <field type="CharField" name="branch">{{branch}}</field>
147    <field type="CharField" name="dirpath">{{dirpath}}</field>
148  </object>
149'''
150
151layer_version_HEAD_template = '''\
152  <object model="orm.layer_version" pk="{{lv_count}}">
153    <field rel="ManyToOneRel" to="orm.layer" name="layer">{{layer_id}}</field>
154    <field type="IntegerField" name="layer_source">0</field>
155    <field rel="ManyToOneRel" to="orm.release" name="release">{{release_id}}</field>
156    <field type="CharField" name="branch">{{branch}}</field>
157    <field type="CharField" name="commit">{{commit}}</field>
158    <field type="CharField" name="dirpath">{{dirpath}}</field>
159  </object>
160'''
161
162layer_version_oe_core_template = '''\
163  <object model="orm.layer_version" pk="1">
164    <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field>
165    <field rel="ManyToOneRel" to="orm.release" name="release">2</field>
166    <field type="CharField" name="local_path">OE-CORE-LAYER-DIR</field>
167    <field type="CharField" name="branch">HEAD</field>
168    <field type="CharField" name="dirpath">meta</field>
169    <field type="IntegerField" name="layer_source">0</field>
170  </object>
171'''
172
173epilog_template = '''\
174</django-objects>
175'''
176
177#################################
178# Helper Routines
179#
180
181def print_str(str,fd):
182    # Avoid extra newline at end
183    if str and (str[-1] == '\n'):
184        str = str[0:-1]
185    print(str,file=fd)
186
187def print_template(template,params,fd):
188    for line in template.split('\n'):
189        p = line.find('{{')
190        while p > 0:
191            q = line.find('}}')
192            key = line[p+2:q]
193            if key in params:
194                line = line[0:p] + params[key] + line[q+2:]
195            else:
196                line = line[0:p] + '?' + key + '?' + line[q+2:]
197            p = line.find('{{')
198        if line:
199            print(line,file=fd)
200
201#################################
202# Generate poky.xml
203#
204
205def generate_poky():
206    fd = open('poky.xml','w')
207
208    params = {}
209    params['distro'] = 'poky'
210    print_template(prolog_template,params,fd)
211    print_str('',fd)
212
213    print_str('  <!-- Bitbake versions which correspond to the metadata release -->',fd)
214    for i,release in enumerate(current_releases):
215        params = {}
216        params['release'] = release[0]
217        params['Release'] = release[0]
218        params['release_version'] = release[1]
219        if not (params['release'] in ('HEAD')):    # 'master',
220            params['release'] = params['release'][0].lower() + params['release'][1:]
221        params['name'] = params['release']
222        params['bitbake_id'] = str(i+1)
223        params['branch'] = params['release']
224        print_template(bitbakeversion_poky_template,params,fd)
225    print_str('',fd)
226
227    print_str('',fd)
228    print_str('  <!-- Releases available -->',fd)
229    for i,release in enumerate(current_releases):
230        params = {}
231        params['release'] = release[0]
232        params['Release'] = release[0]
233        params['release_version'] = release[1]
234        if not (params['release'] in ('HEAD')): #'master',
235            params['release'] = params['release'][0].lower() + params['release'][1:]
236        params['h_release'] = '?h={{release}}'
237        params['name'] = params['release']
238        params['ra_count'] = str(i+1)
239        params['branch'] = params['release']
240
241        if 'HEAD' == params['release']:
242            params['help_source'] = 'with the version of the Yocto Project you have cloned or downloaded to your computer'
243            params['description'] = 'Local Yocto Project'
244            params['name'] = 'local'
245        else:
246            params['help_source'] = 'using the tip of the &lt;a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/{{h_release}}"&gt;Yocto Project {{Release}} branch&lt;/a&gt;'
247            params['description'] = 'Yocto Project {{release_version}} "{{Release}}"'
248        if 'master' == params['release']:
249            params['h_release'] = ''
250            params['description'] = 'Yocto Project master'
251
252        print_template(releases_available_template,params,fd)
253    print_str('',fd)
254
255    print_str('  <!-- Default project layers for each release -->',fd)
256    rdl_count = 1
257    for i,release in enumerate(current_releases):
258        for j,layer in enumerate(default_poky_layers):
259            params = {}
260            params['layer'] = layer
261            params['release'] = release[0]
262            params['Release'] = release[0]
263            params['release_version'] = release[1]
264            if not (params['release'] in ('master','HEAD')):
265                params['release'] = params['release'][0].lower() + params['release'][1:]
266            params['release_id'] = str(i+1)
267            params['rdl_count'] = str(rdl_count)
268            params['branch'] = params['release']
269            print_template(default_layers_template,params,fd)
270            rdl_count += 1
271    print_str('',fd)
272
273    print_str(default_layers_preface,fd)
274    lv_count = 1
275    for i,layer in enumerate(default_poky_layers):
276        params = {}
277        params['layer'] = layer
278        params['layer_id'] = str(i+1)
279        params['vcs_url'] = 'git://git.yoctoproject.org/poky'
280        params['vcs_web_url'] = 'https://git.yoctoproject.org/cgit/cgit.cgi/poky'
281        params['vcs_web_tree_base_url'] = 'https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%'
282        params['vcs_web_file_base_url'] = 'https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%'
283
284        if i:
285            print_str('',fd)
286        print_template(layer_poky_template,params,fd)
287        for j,release in enumerate(current_releases):
288            params['release'] = release[0]
289            params['Release'] = release[0]
290            params['release_version'] = release[1]
291            if not (params['release'] in ('master','HEAD')):
292                params['release'] = params['release'][0].lower() + params['release'][1:]
293            params['release_id'] = str(j+1)
294            params['lv_count'] = str(lv_count)
295            params['branch'] = params['release']
296            params['commit'] = params['release']
297
298            params['dirpath'] = params['layer']
299            if params['layer'] in ('openembedded-core'):   #'openembedded-core',
300                params['dirpath'] = 'meta'
301
302            if 'HEAD' == params['release']:
303                print_template(layer_version_HEAD_template,params,fd)
304            else:
305                print_template(layer_version_template,params,fd)
306            lv_count += 1
307
308    print_str(epilog_template,fd)
309    fd.close()
310
311#################################
312# Generate oe-core.xml
313#
314
315def generate_oe_core():
316    fd = open('oe-core.xml','w')
317
318    params = {}
319    params['distro'] = 'nodistro'
320    print_template(prolog_template,params,fd)
321    print_str('',fd)
322
323    print_str('  <!-- Bitbake versions which correspond to the metadata release -->',fd)
324    for i,release in enumerate(current_releases):
325        params = {}
326        params['release'] = release[0]
327        params['Release'] = release[0]
328        params['bitbakeversion'] = release[6]
329        params['release_version'] = release[1]
330        if not (params['release'] in ('HEAD')):    # 'master',
331            params['release'] = params['release'][0].lower() + params['release'][1:]
332        params['name'] = params['release']
333        params['bitbake_id'] = str(i+1)
334        params['branch'] = params['release']
335        print_template(bitbakeversion_oecore_template,params,fd)
336    print_str('',fd)
337
338    print_str('  <!-- Releases available -->',fd)
339    for i,release in enumerate(current_releases):
340        params = {}
341        params['release'] = release[0]
342        params['Release'] = release[0]
343        params['release_version'] = release[1]
344        if not (params['release'] in ('HEAD')): #'master',
345            params['release'] = params['release'][0].lower() + params['release'][1:]
346        params['h_release'] = '?h={{release}}'
347        params['name'] = params['release']
348        params['ra_count'] = str(i+1)
349        params['branch'] = params['release']
350
351        if 'HEAD' == params['release']:
352            params['help_source'] = 'with the version of OpenEmbedded that you have cloned or downloaded to your computer'
353            params['description'] = 'Local Openembedded'
354            params['name'] = 'local'
355        else:
356            params['help_source'] = 'using the tip of the &lt;a href=\\"https://cgit.openembedded.org/openembedded-core/log/{{h_release}}\\"&gt;OpenEmbedded {{Release}}&lt;/a&gt; branch'
357            params['description'] = 'Openembedded {{Release}}'
358        if 'master' == params['release']:
359            params['h_release'] = ''
360            params['description'] = 'OpenEmbedded core master'
361            params['Release'] = params['release']
362
363        print_template(releases_available_template,params,fd)
364    print_str('',fd)
365
366    print_str('  <!-- Default layers for each release -->',fd)
367    rdl_count = 1
368    for i,release in enumerate(current_releases):
369        for j,layer in enumerate(default_oe_core_layers):
370            params = {}
371            params['layer'] = layer
372            params['release'] = release[0]
373            params['Release'] = release[0]
374            params['release_version'] = release[1]
375            if not (params['release'] in ('master','HEAD')):
376                params['release'] = params['release'][0].lower() + params['release'][1:]
377            params['release_id'] = str(i+1)
378            params['rdl_count'] = str(rdl_count)
379            params['branch'] = params['release']
380            print_template(default_layers_template,params,fd)
381            rdl_count += 1
382    print_str('',fd)
383
384    print_str('',fd)
385    print_str('  <!-- Layer for the Local release -->',fd)
386    lv_count = 1
387    for i,layer in enumerate(default_oe_core_layers):
388        params = {}
389        params['layer'] = layer
390        params['layer_id'] = str(i+1)
391        params['vcs_url'] = 'git://git.openembedded.org/openembedded-core'
392        params['vcs_web_url'] = 'https://cgit.openembedded.org/openembedded-core'
393        params['vcs_web_tree_base_url'] = 'https://cgit.openembedded.org/openembedded-core/tree/%path%?h=%branch%'
394        params['vcs_web_file_base_url'] = 'https://cgit.openembedded.org/openembedded-core/tree/%path%?h=%branch%'
395        if i:
396            print_str('',fd)
397        print_template(layer_oe_core_template,params,fd)
398
399        print_template(layer_version_oe_core_template,params,fd)
400    print_str('',fd)
401
402    print_str(epilog_template,fd)
403    fd.close()
404
405#################################
406# Help
407#
408
409def list_releases():
410    print("Release    ReleaseVer  BitbakeVer Support Level")
411    print("========== =========== ========== ==============================================")
412    for release in current_releases:
413        print("%10s %10s %11s %s" % (release[0],release[1],release[6],release[4]))
414
415#################################
416# main
417#
418
419def main(argv):
420    global verbose
421
422    parser = argparse.ArgumentParser(description='gen_fixtures.py: table generate the fixture files')
423    parser.add_argument('--poky', '-p', action='store_const', const='poky', dest='command', help='Generate the poky.xml file')
424    parser.add_argument('--oe-core', '-o', action='store_const', const='oe_core', dest='command', help='Generate the oe-core.xml file')
425    parser.add_argument('--all', '-a', action='store_const', const='all', dest='command', help='Generate all fixture files')
426    parser.add_argument('--list', '-l', action='store_const', const='list', dest='command', help='List the release table')
427    parser.add_argument('--verbose', '-v', action='store_true', dest='verbose', help='Enable verbose debugging output')
428    args = parser.parse_args()
429
430    verbose = args.verbose
431    if 'poky' == args.command:
432        generate_poky()
433    elif 'oe_core' == args.command:
434        generate_oe_core()
435    elif 'all' == args.command:
436        generate_poky()
437        generate_oe_core()
438    elif 'all' == args.command:
439        list_releases()
440    elif 'list' == args.command:
441        list_releases()
442
443    else:
444        print("No command for 'gen_fixtures.py' selected")
445
446if __name__ == '__main__':
447    main(sys.argv[1:])
448