Coverage for apio/__main__.py: 89%

35 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-07 05:08 +0000

1# This file is part of the Apio project 

2# (C) 2016-present FPGAwars 

3# Project authors: 

4# * Jesús Arroyo (Jesus89) (2016-2019) 

5# * Juan Gonzalez (obijuan) (2019-2024) 

6# * The Apio Team (2024-present) 

7# License: GPLv2 

8 

9"""Apio starting point.""" 

10 

11import sys 

12import atexit 

13from apio.common.debug_util import is_debug 

14 

15# -- IMPORTANT: This module is an entry point for both the Apio 

16# -- process and the Scons process (when using under pyinstaller) and 

17# -- therefore can depend only on common functionality under 

18# -- apio.common. 

19 

20 

21debug_enabled = is_debug(1) 

22 

23 

24def on_exit(msg): 

25 """Prints a debug message on process exit. The msg string is passed 

26 from the handler registrations below. 

27 """ 

28 if debug_enabled: 

29 print(msg) 

30 

31 

32def main(): 

33 """Apio starting point.""" 

34 

35 if debug_enabled: 

36 print(f"Apio main(): original argv: {sys.argv}") 

37 

38 # pylint: disable=import-outside-toplevel 

39 

40 # -- Handle the case of the scons subprocess. Because we also use 

41 # -- pyinstaller, without standard pip packages, we can't simply invoke 

42 # -- the 'scons' binary so we invoke the SCons module from the apio main. 

43 # -- See more details at: 

44 # -- See https://github.com/orgs/pyinstaller/discussions/9023. 

45 # -- 

46 # -- In this case argv goes through these stages 

47 # -- Original: <binary> -m apio --scons ... 

48 # -- Under python: <binary> --scons ... 

49 # -- Under pyinstaller: <binary> -m apio --scons ... 

50 # -- After fixing for scons: <binary> ... 

51 # -- 

52 # -- Notice that the -m apio args are automatically removed by the 

53 # -- python interpreter but are preserved by the pyinstaller, hence 

54 # -- the two cases. 

55 python_scons = sys.argv[1:2] == ["--scons"] 

56 pyinstaller_scons = sys.argv[1:4] == ["-m", "apio", "--scons"] 

57 

58 if debug_enabled: 

59 print(f"Apio main(): {python_scons=}") 

60 print(f"Apio main(): {pyinstaller_scons=}") 

61 

62 if python_scons or pyinstaller_scons: 

63 

64 if debug_enabled: 64 ↛ 65line 64 didn't jump to line 65 because the condition on line 64 was never true

65 print("Apio main(): this is an scons process") 

66 

67 # -- Since scons_main() doesn't return, we use this handler to print 

68 # -- an exit message for debugging. 

69 atexit.register(on_exit, "Apio main(): scons process exit") 

70 

71 # -- Import and initialize scons only when running the scons 

72 # -- subprocess. 

73 from SCons.Script.Main import main as scons_main 

74 from apio.common.common_util import maybe_wait_for_remote_debugger 

75 

76 # -- If system env var APIO_SCONS_DEBUGGER is defined, regardless of 

77 # -- its value, we wait on a remote debugger to be attached, e.g. 

78 # -- from Visual Studio Code. 

79 # -- 

80 # -- You can place a breakpoint for example at SconsHandler.start(). 

81 maybe_wait_for_remote_debugger("APIO_SCONS_DEBUGGER") 

82 

83 # -- Drop the scons trigger args 

84 if python_scons: 84 ↛ 91line 84 didn't jump to line 91 because the condition on line 84 was always true

85 # -- Case 1: Using a python interpreter which already dropped 

86 # -- the ["-m", "apio"]. Dropping just ["--scons"] 

87 sys.argv[1:] = sys.argv[2:] 

88 else: 

89 # -- Case 2: Using pyinstaller which preserved the ["-m", "apio"]. 

90 # -- Dropping ["-m", "apio", "--scons"] 

91 sys.argv[1:] = sys.argv[4:] 

92 

93 if debug_enabled: 93 ↛ 94line 93 didn't jump to line 94 because the condition on line 93 was never true

94 print(f"Apio main(): scons fixed argv: {sys.argv}") 

95 

96 # -- Invoke the scons main function. It gets the modified argv from sys 

97 # -- and doesn't return. 

98 scons_main() 

99 

100 # -- Handle the case of a normal apio invocation. 

101 else: 

102 if debug_enabled: 

103 print("Apio main(): this is an apio process") 

104 

105 # -- Since apio_top_cli() doesn't return, we use this handler to print 

106 # -- an exit message for debugging. 

107 atexit.register(on_exit, "Apio main(): apio process exit") 

108 

109 # -- Import the apio CLI only when running the apio process 

110 # -- (as opposed to the scons sub process). 

111 from apio.commands.apio import apio_top_cli 

112 

113 # -- Due to the Click decorations of apio_top_cli() and the Click 

114 # -- magic, this function is not really invoked but Click dispatches 

115 # -- it to its subcommands that was selected by the user command line. 

116 # -- This function call doesn't return. 

117 apio_top_cli() 

118 

119 

120if __name__ == "__main__": 

121 main()