5454LATEST_LTS_MAJOR = 6
5555LATEST_LTS_MINOR = 12
5656
57+ # Prefix marking a fragment entry as a kernel make target generating
58+ # config (e.g. 'make:kselftest-merge') rather than a config symbol.
59+ MAKE_FRAGMENT_PREFIX = "make:"
60+
5761DTBS_DISABLED = {
5862 "i386" : True ,
5963 "x86_64" : True ,
@@ -618,13 +622,44 @@ def add_fragment(self, fragname):
618622 print (f"Using fragment { fragname } from inline configs" )
619623 return self .extract_config (frag )
620624
625+ @staticmethod
626+ def _split_fragment (content ):
627+ """Split fragment content into make targets and config symbols
628+
629+ A fragment entry prefixed with 'make:' names a kernel make target
630+ generating config, such as 'make:kselftest-merge', rather than a
631+ config symbol. Those entries must be kept out of the fragment file:
632+ kconfig does not understand them and merges them as "unexpected
633+ data", silently dropping the config the fragment is meant to add.
634+
635+ Returns:
636+ tuple: (list of make targets, config symbol text)
637+ """
638+ make_targets = []
639+ config_lines = []
640+
641+ for line in content .splitlines ():
642+ entry = line .strip ()
643+ if entry .startswith (MAKE_FRAGMENT_PREFIX ):
644+ target = entry [len (MAKE_FRAGMENT_PREFIX ) :]
645+ if target :
646+ make_targets .append (target )
647+ else :
648+ config_lines .append (line )
649+
650+ config = "\n " .join (config_lines ).strip ()
651+ if config :
652+ config += "\n "
653+ return make_targets , config
654+
621655 def _parse_fragments (self , firmware = False ):
622656 """Parse fragments kbuild config and create config fragments
623657
624658 Returns:
625- list: List of fragment file paths
659+ list: List of kconfig additions, each either a fragment file
660+ path or a 'make:<target>' directive, in merge order
626661 """
627- fragment_files = []
662+ kconfig_adds = []
628663
629664 for idx , fragment in enumerate (self ._fragments ):
630665 content = ""
@@ -646,23 +681,34 @@ def _parse_fragments(self, firmware=False):
646681 )
647682 continue
648683
649- fragfile = os .path .join (self ._fragments_dir , f"{ idx } .config" )
650- with open (fragfile , "w" ) as f :
651- f .write (content )
684+ make_targets , config = self ._split_fragment (content )
652685
653- config_count = len (
654- [line for line in content .split ("\n " ) if line .strip ()]
655- )
656- print (
657- f"[_parse_fragments] Created { fragfile } ({ config_count } configs)"
658- )
686+ if config :
687+ fragfile = os .path .join (self ._fragments_dir , f"{ idx } .config" )
688+ with open (fragfile , "w" ) as f :
689+ f .write (config )
659690
660- fragment_files .append (fragfile )
691+ config_count = len (
692+ [line for line in config .split ("\n " ) if line .strip ()]
693+ )
694+ print (
695+ f"[_parse_fragments] Created { fragfile } ({ config_count } configs)"
696+ )
697+
698+ kconfig_adds .append (fragfile )
699+
700+ # add fragment to artifacts but relative to artifacts dir
701+ frag_rel = os .path .relpath (fragfile , self ._af_dir )
702+ self ._artifacts .append (frag_rel )
703+
704+ for target in make_targets :
705+ print (
706+ f"[_parse_fragments] Fragment { fragment_name } runs "
707+ f"make target { target } "
708+ )
709+ kconfig_adds .append (MAKE_FRAGMENT_PREFIX + target )
661710
662- # add fragment to artifacts but relative to artifacts dir
663- frag_rel = os .path .relpath (fragfile , self ._af_dir )
664711 self ._config_full += "+" + fragment_name
665- self ._artifacts .append (frag_rel )
666712
667713 if firmware :
668714 content = 'CONFIG_EXTRA_FIRMWARE_DIR="' + self ._firmware_dir + '"\n '
@@ -672,22 +718,23 @@ def _parse_fragments(self, firmware=False):
672718 with open (fragfile , "w" ) as f :
673719 f .write (content )
674720
675- fragment_files .append (fragfile )
721+ kconfig_adds .append (fragfile )
676722
677723 # add fragment to artifacts but relative to artifacts dir
678724 frag_rel = os .path .relpath (fragfile , self ._af_dir )
679725 self ._artifacts .append (frag_rel )
680726
681727 print (
682- f"[_parse_fragments] Created { len (fragment_files )} fragment files "
728+ f"[_parse_fragments] Created { len (kconfig_adds )} kconfig additions "
683729 )
684- return fragment_files
730+ return kconfig_adds
685731
686- def _merge_frags (self , fragment_files ):
732+ def _merge_frags (self , kconfig_adds ):
687733 """Merge config fragments to .config
688734
689735 Args:
690- fragment_files: List of fragment file paths to merge
736+ kconfig_adds: List of kconfig additions, as returned by
737+ _parse_fragments()
691738 """
692739 self .startjob ("config_defconfig" )
693740 self .addcmd ("cd " + self ._srcdir )
@@ -715,10 +762,14 @@ def _merge_frags(self, fragment_files):
715762 self ._config_full = defconfigs + self ._config_full
716763 # fragments
717764 self .startjob ("config_fragments" )
718- for fragfile in fragment_files :
719- self .addcmd (
720- f"./scripts/kconfig/merge_config.sh -m .config { fragfile } "
721- )
765+ for entry in kconfig_adds :
766+ if entry .startswith (MAKE_FRAGMENT_PREFIX ):
767+ # the target merges its own config into the .config built
768+ # so far, so run it in place of a merge_config.sh call
769+ target = entry [len (MAKE_FRAGMENT_PREFIX ) :]
770+ self .addcmd (f"make { target } " )
771+ continue
772+ self .addcmd (f"./scripts/kconfig/merge_config.sh -m .config { entry } " )
722773 # TODO: olddefconfig should be optional/configurable
723774 # TODO: log all warnings/errors of olddefconfig to separate file
724775 self .addcmd ("make olddefconfig" )
@@ -729,12 +780,12 @@ def _merge_frags(self, fragment_files):
729780 def _generate_script (self ):
730781 """Generate shell script for complete build"""
731782 print ("Generating shell script" )
732- self ._fragment_files = self ._parse_fragments (firmware = True )
783+ self ._kconfig_adds = self ._parse_fragments (firmware = True )
733784
734785 if self ._backend == "tuxmake" :
735786 self ._build_with_tuxmake ()
736787 else :
737- self ._merge_frags (self ._fragment_files )
788+ self ._merge_frags (self ._kconfig_adds )
738789 self ._build_with_make ()
739790
740791 self ._write_metadata ()
@@ -800,12 +851,12 @@ def _build_with_tuxmake(self):
800851 """Build kernel using tuxmake with native fragment support"""
801852 print ("[_build_with_tuxmake] Starting tuxmake build" )
802853
803- if not hasattr (self , "_fragment_files " ):
804- print ("[_build_with_tuxmake] ERROR: No fragment files available" )
805- self ._fragment_files = []
854+ if not hasattr (self , "_kconfig_adds " ):
855+ print ("[_build_with_tuxmake] ERROR: No kconfig additions available" )
856+ self ._kconfig_adds = []
806857
807858 print (
808- f"[_build_with_tuxmake] Using { len (self ._fragment_files )} fragment files "
859+ f"[_build_with_tuxmake] Using { len (self ._kconfig_adds )} kconfig additions "
809860 )
810861
811862 # Handle defconfigs - first goes to --kconfig, rest to --kconfig-add
@@ -931,17 +982,20 @@ def _tuxmake_base(self, output_dir, defconfig, extra_defconfigs):
931982 for extra in extra_defconfigs :
932983 parts .append (f"--kconfig-add={ extra } " )
933984 print (f"[_tuxmake_base] Adding extra defconfig: { extra } " )
934- for fragfile in self ._fragment_files :
935- if os .path .exists (fragfile ):
936- parts .append (f"--kconfig-add={ fragfile } " )
985+ for entry in self ._kconfig_adds :
986+ if entry .startswith (MAKE_FRAGMENT_PREFIX ):
987+ # tuxmake runs the make target during config preparation
988+ parts .append (f"--kconfig-add={ entry } " )
989+ print (f"[_tuxmake_base] Adding make target: { entry } " )
990+ elif os .path .exists (entry ):
991+ parts .append (f"--kconfig-add={ entry } " )
937992 print (
938993 "[_tuxmake_base] Adding fragment: "
939- f"{ os .path .basename (fragfile )} "
994+ f"{ os .path .basename (entry )} "
940995 )
941996 else :
942997 print (
943- "[_tuxmake_base] WARNING: Fragment file not found: "
944- f"{ fragfile } "
998+ f"[_tuxmake_base] WARNING: Fragment file not found: { entry } "
945999 )
9461000 return parts
9471001
0 commit comments