build_pytest_apps.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Apache-2.0
  3. """
  4. This file is used to generate binary files for the given path.
  5. """
  6. import argparse
  7. import copy
  8. import logging
  9. import os
  10. import sys
  11. from collections import defaultdict
  12. from typing import List
  13. from idf_ci_utils import IDF_PATH, get_pytest_cases
  14. try:
  15. from build_apps import build_apps
  16. from find_apps import find_builds_for_app
  17. from find_build_apps import BuildItem, config_rules_from_str, setup_logging
  18. except ImportError:
  19. sys.path.append(os.path.join(IDF_PATH, 'tools'))
  20. from build_apps import build_apps
  21. from find_apps import find_builds_for_app
  22. from find_build_apps import BuildItem, config_rules_from_str, setup_logging
  23. def main(args: argparse.Namespace) -> None:
  24. pytest_cases = []
  25. for path in args.paths:
  26. pytest_cases += get_pytest_cases(path, args.target)
  27. paths = set()
  28. app_configs = defaultdict(set)
  29. for case in pytest_cases:
  30. paths.add(case.app_path)
  31. app_configs[case.app_path].add(case.config)
  32. app_dirs = list(paths)
  33. if not app_dirs:
  34. raise RuntimeError('No apps found')
  35. logging.info(f'Found {len(app_dirs)} apps')
  36. app_dirs.sort()
  37. # Find compatible configurations of each app, collect them as BuildItems
  38. build_items: List[BuildItem] = []
  39. config_rules = config_rules_from_str(args.config or [])
  40. for app_dir in app_dirs:
  41. app_dir = os.path.realpath(app_dir)
  42. build_items += find_builds_for_app(
  43. app_path=app_dir,
  44. work_dir=app_dir,
  45. build_dir='build_@t_@w',
  46. build_log=f'{app_dir}/build_@t_@w/build.log',
  47. target_arg=args.target,
  48. build_system='cmake',
  49. config_rules=config_rules,
  50. )
  51. modified_build_items = []
  52. # auto clean up the binaries if no flag --preserve-all
  53. for item in build_items:
  54. is_test_related = item.config_name in app_configs[item.app_dir]
  55. if args.test_only and not is_test_related:
  56. logging.info(f'Skipping non-test app: {item}')
  57. continue
  58. copied_item = copy.deepcopy(item)
  59. if not args.preserve_all and not is_test_related:
  60. copied_item.preserve = False
  61. modified_build_items.append(copied_item)
  62. logging.info(f'Found {len(modified_build_items)} builds')
  63. modified_build_items.sort(key=lambda x: x.build_path) # type: ignore
  64. build_apps(
  65. build_items=modified_build_items,
  66. parallel_count=args.parallel_count,
  67. parallel_index=args.parallel_index,
  68. dry_run=False,
  69. build_verbose=args.build_verbose,
  70. keep_going=True,
  71. output_build_list=None,
  72. size_info=args.size_info,
  73. )
  74. if __name__ == '__main__':
  75. parser = argparse.ArgumentParser(
  76. description='Build all the pytest apps under specified paths. Will auto remove those non-test apps binaries'
  77. )
  78. parser.add_argument('--target', required=True, help='Build apps for given target.')
  79. parser.add_argument(
  80. '--config',
  81. default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'],
  82. action='append',
  83. help='Adds configurations (sdkconfig file names) to build. This can either be '
  84. + 'FILENAME[=NAME] or FILEPATTERN. FILENAME is the name of the sdkconfig file, '
  85. + 'relative to the project directory, to be used. Optional NAME can be specified, '
  86. + 'which can be used as a name of this configuration. FILEPATTERN is the name of '
  87. + 'the sdkconfig file, relative to the project directory, with at most one wildcard. '
  88. + 'The part captured by the wildcard is used as the name of the configuration.',
  89. )
  90. parser.add_argument(
  91. 'paths',
  92. nargs='+',
  93. help='One or more app paths. Will use the current path if not specified.',
  94. )
  95. parser.add_argument(
  96. '--parallel-count', default=1, type=int, help='Number of parallel build jobs.'
  97. )
  98. parser.add_argument(
  99. '--parallel-index',
  100. default=1,
  101. type=int,
  102. help='Index (1-based) of the job, out of the number specified by --parallel-count.',
  103. )
  104. parser.add_argument(
  105. '--size-info',
  106. type=argparse.FileType('a'),
  107. help='If specified, the test case name and size info json will be written to this file',
  108. )
  109. parser.add_argument(
  110. '-v',
  111. '--verbose',
  112. action='count',
  113. help='Increase the logging level of the script. Can be specified multiple times.',
  114. )
  115. parser.add_argument(
  116. '--build-verbose',
  117. action='store_true',
  118. help='Enable verbose output from build system.',
  119. )
  120. parser.add_argument(
  121. '--preserve-all',
  122. action='store_true',
  123. help='Preserve the binaries for all apps when specified.',
  124. )
  125. parser.add_argument(
  126. '--test-only',
  127. action='store_true',
  128. help='Build only test related app when specified.',
  129. )
  130. arguments = parser.parse_args()
  131. setup_logging(arguments)
  132. main(arguments)