zero-build 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. zero_build-0.1.0/PKG-INFO +64 -0
  2. zero_build-0.1.0/README.md +54 -0
  3. zero_build-0.1.0/pyproject.toml +25 -0
  4. zero_build-0.1.0/setup.cfg +4 -0
  5. zero_build-0.1.0/src/zero/__init__.py +20 -0
  6. zero_build-0.1.0/src/zero/analyzers/__init__.py +0 -0
  7. zero_build-0.1.0/src/zero/analyzers/cycle_detector.py +146 -0
  8. zero_build-0.1.0/src/zero/analyzers/stale_detector.py +167 -0
  9. zero_build-0.1.0/src/zero/builder/__init__.py +0 -0
  10. zero_build-0.1.0/src/zero/builder/builder.py +175 -0
  11. zero_build-0.1.0/src/zero/cli/__init__.py +0 -0
  12. zero_build-0.1.0/src/zero/cli/__main__.py +5 -0
  13. zero_build-0.1.0/src/zero/cli/args.py +60 -0
  14. zero_build-0.1.0/src/zero/cli/main.py +20 -0
  15. zero_build-0.1.0/src/zero/compilers/__init__.py +6 -0
  16. zero_build-0.1.0/src/zero/compilers/base.py +36 -0
  17. zero_build-0.1.0/src/zero/compilers/clang.py +7 -0
  18. zero_build-0.1.0/src/zero/compilers/clangxx.py +7 -0
  19. zero_build-0.1.0/src/zero/compilers/gcc.py +110 -0
  20. zero_build-0.1.0/src/zero/compilers/get.py +25 -0
  21. zero_build-0.1.0/src/zero/compilers/gxx.py +7 -0
  22. zero_build-0.1.0/src/zero/compilers/types.py +9 -0
  23. zero_build-0.1.0/src/zero/errors/__init__.py +4 -0
  24. zero_build-0.1.0/src/zero/errors/errors.py +19 -0
  25. zero_build-0.1.0/src/zero/graph/__init__.py +0 -0
  26. zero_build-0.1.0/src/zero/graph/constructor.py +244 -0
  27. zero_build-0.1.0/src/zero/graph/nodes.py +125 -0
  28. zero_build-0.1.0/src/zero/graph/printer.py +100 -0
  29. zero_build-0.1.0/src/zero/graph/visitor.py +51 -0
  30. zero_build-0.1.0/src/zero/interface/__init__.py +0 -0
  31. zero_build-0.1.0/src/zero/interface/build.py +49 -0
  32. zero_build-0.1.0/src/zero/interface/executable.py +14 -0
  33. zero_build-0.1.0/src/zero/interface/headers.py +57 -0
  34. zero_build-0.1.0/src/zero/interface/library.py +11 -0
  35. zero_build-0.1.0/src/zero/interface/precomp_lib.py +21 -0
  36. zero_build-0.1.0/src/zero/interface/shared_lib.py +12 -0
  37. zero_build-0.1.0/src/zero/interface/source.py +27 -0
  38. zero_build-0.1.0/src/zero/interface/static_lib.py +12 -0
  39. zero_build-0.1.0/src/zero/interface/target.py +90 -0
  40. zero_build-0.1.0/src/zero/interface/types.py +4 -0
  41. zero_build-0.1.0/src/zero/orchestrator/__init__.py +1 -0
  42. zero_build-0.1.0/src/zero/orchestrator/config.py +26 -0
  43. zero_build-0.1.0/src/zero/orchestrator/executor.py +14 -0
  44. zero_build-0.1.0/src/zero/orchestrator/orchestrator.py +204 -0
  45. zero_build-0.1.0/src/zero/py.typed +0 -0
  46. zero_build-0.1.0/src/zero/reporter/__init__.py +2 -0
  47. zero_build-0.1.0/src/zero/reporter/get.py +9 -0
  48. zero_build-0.1.0/src/zero/reporter/reporter.py +43 -0
  49. zero_build-0.1.0/src/zero/utils/__init__.py +1 -0
  50. zero_build-0.1.0/src/zero/utils/module_loader.py +36 -0
  51. zero_build-0.1.0/src/zero_build.egg-info/PKG-INFO +64 -0
  52. zero_build-0.1.0/src/zero_build.egg-info/SOURCES.txt +54 -0
  53. zero_build-0.1.0/src/zero_build.egg-info/dependency_links.txt +1 -0
  54. zero_build-0.1.0/src/zero_build.egg-info/entry_points.txt +2 -0
  55. zero_build-0.1.0/src/zero_build.egg-info/requires.txt +1 -0
  56. zero_build-0.1.0/src/zero_build.egg-info/top_level.txt +1 -0
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: zero-build
3
+ Version: 0.1.0
4
+ Summary: A clean, pythonic, DAG-based C/C++ build system.
5
+ Author-email: Haseeb <haseeeb8j@gmail.com>
6
+ Project-URL: Homepage, https://github.com/kxmtkw/Zero
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: rich>=13.0.0
10
+
11
+
12
+ # Zero - Super Easy Build System
13
+
14
+ `Zero` is a C/C++ build system designed to be as `easy` as possible to use, without lacking any features. Written in python!
15
+
16
+ The only reason `Zero` exists is because I hate CMake syntax.
17
+
18
+ ## Usage
19
+
20
+ First, create a `zero.py` file in your project root and import `zero`
21
+
22
+ ```python
23
+ from zero import *
24
+ ```
25
+
26
+ Create a `Build` object named `build`.
27
+
28
+ ```python
29
+ build = Build()
30
+ ```
31
+
32
+ Configure the `build` object by specifying the compiler and the build directory.
33
+
34
+ ```python
35
+ build.compiler = "gcc"
36
+ build.directory = "build" # or Path("build") also works!
37
+ ```
38
+
39
+ To create an executable, create an `Executable` object! Specify the sources an arguments!
40
+
41
+ ```python
42
+ main = Executable()
43
+ main.source = Source(
44
+ "src/main.c",
45
+ "src/utils.c" # or Path("src") / "utils.c" also works!
46
+ )
47
+ main.arguments = "-Wall", "-O3"
48
+ ```
49
+
50
+ And you are done! Yes that's all.
51
+ Run the `zero` cli tool to make your executable.
52
+ ```bash
53
+ zero make
54
+ ```
55
+ A binary named `main` can now be found at `build/bin/main`
56
+
57
+ ## Installation
58
+
59
+ `Zero` is still in its infant stage. Proper installation does not exist. To still install, run:
60
+ ```bash
61
+ git clone https://github.com/kxmtkw/Zero.git zero
62
+ cd zero
63
+ pip install --break-system-packages -e .
64
+ ```
@@ -0,0 +1,54 @@
1
+
2
+ # Zero - Super Easy Build System
3
+
4
+ `Zero` is a C/C++ build system designed to be as `easy` as possible to use, without lacking any features. Written in python!
5
+
6
+ The only reason `Zero` exists is because I hate CMake syntax.
7
+
8
+ ## Usage
9
+
10
+ First, create a `zero.py` file in your project root and import `zero`
11
+
12
+ ```python
13
+ from zero import *
14
+ ```
15
+
16
+ Create a `Build` object named `build`.
17
+
18
+ ```python
19
+ build = Build()
20
+ ```
21
+
22
+ Configure the `build` object by specifying the compiler and the build directory.
23
+
24
+ ```python
25
+ build.compiler = "gcc"
26
+ build.directory = "build" # or Path("build") also works!
27
+ ```
28
+
29
+ To create an executable, create an `Executable` object! Specify the sources an arguments!
30
+
31
+ ```python
32
+ main = Executable()
33
+ main.source = Source(
34
+ "src/main.c",
35
+ "src/utils.c" # or Path("src") / "utils.c" also works!
36
+ )
37
+ main.arguments = "-Wall", "-O3"
38
+ ```
39
+
40
+ And you are done! Yes that's all.
41
+ Run the `zero` cli tool to make your executable.
42
+ ```bash
43
+ zero make
44
+ ```
45
+ A binary named `main` can now be found at `build/bin/main`
46
+
47
+ ## Installation
48
+
49
+ `Zero` is still in its infant stage. Proper installation does not exist. To still install, run:
50
+ ```bash
51
+ git clone https://github.com/kxmtkw/Zero.git zero
52
+ cd zero
53
+ pip install --break-system-packages -e .
54
+ ```
@@ -0,0 +1,25 @@
1
+ [project]
2
+ name = "zero-build"
3
+ version = "0.1.0"
4
+ description = "A clean, pythonic, DAG-based C/C++ build system."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ authors = [
8
+ { name = "Haseeb", email = "haseeeb8j@gmail.com" }
9
+ ]
10
+ dependencies = [
11
+ "rich>=13.0.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ zero = "zero.cli.main:main"
16
+
17
+ [project.urls]
18
+ Homepage = "https://github.com/kxmtkw/Zero"
19
+
20
+ [build-system]
21
+ requires = ["setuptools >= 77.0.3"]
22
+ build-backend = "setuptools.build_meta"
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ # Main API classes
2
+ from pathlib import Path
3
+
4
+ from src.zero.interface.build import Build
5
+ from src.zero.interface.source import Source
6
+ from src.zero.interface.executable import Executable
7
+ from src.zero.interface.static_lib import StaticLibrary
8
+ from src.zero.interface.shared_lib import SharedLibrary
9
+ from src.zero.interface.precomp_lib import PreCompiledLibrary
10
+
11
+ __all__ = [
12
+ "Path",
13
+
14
+ "Build",
15
+ "Source",
16
+ "Executable",
17
+ "StaticLibrary",
18
+ "SharedLibrary",
19
+ "PreCompiledLibrary"
20
+ ]
File without changes
@@ -0,0 +1,146 @@
1
+ from src.zero.graph.nodes import Node
2
+ from src.zero.graph.visitor import NodeVisitor
3
+ from src.zero.graph.nodes import *
4
+
5
+ from src.zero.reporter import getReporter
6
+
7
+
8
+ class CycleDetector(NodeVisitor):
9
+
10
+
11
+ def __init__(self) -> None:
12
+ self.visited_nodes: set[Node] = set()
13
+ self.tracked_nodes: list[Node] = []
14
+ self.is_cycle_detected: bool = False
15
+
16
+ self.reporter = getReporter()
17
+
18
+
19
+ def getCycle(self, node: Node) -> list[Node]:
20
+
21
+ self.is_cycle_detected = True
22
+
23
+ cycle = []
24
+
25
+ for i in range(len(self.tracked_nodes)):
26
+ if self.tracked_nodes[i] == node:
27
+ cycle = self.tracked_nodes[i:]
28
+ cycle.append(node)
29
+ break
30
+
31
+ return cycle
32
+
33
+
34
+ def visit(self, node: Node):
35
+
36
+ if self.is_cycle_detected:
37
+ return
38
+
39
+ return super().visit(node)
40
+
41
+
42
+ def visitRootNode(self, node: RootNode):
43
+
44
+ if node not in self.visited_nodes:
45
+ self.visited_nodes.add(node)
46
+
47
+ self.tracked_nodes.append(node)
48
+
49
+ for t in node.targets:
50
+ self.visit(t)
51
+
52
+ self.tracked_nodes.pop()
53
+
54
+
55
+ def visitExecutableNode(self, node: ExecutableNode):
56
+ if node not in self.visited_nodes:
57
+ self.visited_nodes.add(node)
58
+
59
+ if node in self.tracked_nodes:
60
+ self.getCycle(node)
61
+ return
62
+
63
+ self.tracked_nodes.append(node)
64
+
65
+ for lib in node.linked_libraries:
66
+ self.visit(lib)
67
+
68
+ for src in node.sources:
69
+ self.visit(src)
70
+
71
+ self.tracked_nodes.pop()
72
+
73
+
74
+ def visitStaticLibraryNode(self, node: StaticLibraryNode):
75
+ if node not in self.visited_nodes:
76
+ self.visited_nodes.add(node)
77
+
78
+ if node in self.tracked_nodes:
79
+ self.getCycle(node)
80
+ return
81
+
82
+ self.tracked_nodes.append(node)
83
+
84
+ for lib in node.linked_libraries:
85
+ self.visit(lib)
86
+
87
+ for src in node.sources:
88
+ self.visit(src)
89
+
90
+ self.tracked_nodes.pop()
91
+
92
+
93
+ def visitSharedLibraryNode(self, node: SharedLibraryNode):
94
+ if node not in self.visited_nodes:
95
+ self.visited_nodes.add(node)
96
+
97
+ if node in self.tracked_nodes:
98
+ self.getCycle(node)
99
+ return
100
+
101
+ self.tracked_nodes.append(node)
102
+
103
+ for lib in node.linked_libraries:
104
+ self.visit(lib)
105
+
106
+ for src in node.sources:
107
+ self.visit(src)
108
+
109
+ self.tracked_nodes.pop()
110
+
111
+
112
+ def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
113
+ if node not in self.visited_nodes:
114
+ self.visited_nodes.add(node)
115
+
116
+
117
+ def visitSourceNode(self, node: SourceNode):
118
+ if node not in self.visited_nodes:
119
+ self.visited_nodes.add(node)
120
+
121
+ if node in self.tracked_nodes:
122
+ self.getCycle(node)
123
+ return
124
+
125
+ self.tracked_nodes.append(node)
126
+
127
+ for header in node.deps:
128
+ self.visit(header)
129
+
130
+ self.tracked_nodes.pop()
131
+
132
+
133
+ def visitHeaderNode(self, node: HeaderNode):
134
+ if node not in self.visited_nodes:
135
+ self.visited_nodes.add(node)
136
+
137
+ if node in self.tracked_nodes:
138
+ self.getCycle(node)
139
+ return
140
+
141
+ self.tracked_nodes.append(node)
142
+
143
+ for header in node.deps:
144
+ self.visit(header)
145
+
146
+ self.tracked_nodes.pop()
@@ -0,0 +1,167 @@
1
+ from src.zero.graph.nodes import Node
2
+ from src.zero.graph.visitor import NodeVisitor
3
+ from src.zero.graph.nodes import *
4
+
5
+ from src.zero.reporter import getReporter
6
+
7
+
8
+ def markStale(node: Node):
9
+ setattr(node, "_stale", True)
10
+
11
+
12
+ def isStale(node: Node) -> bool:
13
+ return hasattr(node, "_stale")
14
+
15
+
16
+ class StaleDetector(NodeVisitor):
17
+
18
+
19
+ def __init__(self) -> None:
20
+ self.visited_nodes: set[Node] = set()
21
+ self.current_source_outpath: Path | None = None
22
+ self.stale_count = 0
23
+
24
+
25
+ def markStale(self, node: Node):
26
+ if not hasattr(node, "_stale"):
27
+ setattr(node, "_stale", True)
28
+ self.stale_count += 1
29
+
30
+
31
+ def getStaleCount(self) -> int:
32
+ return self.stale_count
33
+
34
+
35
+ def visitRootNode(self, node: RootNode):
36
+
37
+ if node not in self.visited_nodes:
38
+ self.visited_nodes.add(node)
39
+ else:
40
+ return
41
+
42
+
43
+ for t in node.targets:
44
+ self.visit(t)
45
+
46
+
47
+
48
+ def visitExecutableNode(self, node: ExecutableNode):
49
+
50
+ if node not in self.visited_nodes:
51
+ self.visited_nodes.add(node)
52
+ else:
53
+ return
54
+
55
+
56
+ for lib in node.linked_libraries:
57
+ self.visit(lib)
58
+
59
+ if isStale(lib):
60
+ self.markStale(node)
61
+
62
+ for src in node.sources:
63
+ self.visit(src)
64
+
65
+ if isStale(src):
66
+ self.markStale(node)
67
+
68
+
69
+ def visitStaticLibraryNode(self, node: StaticLibraryNode):
70
+
71
+ if node not in self.visited_nodes:
72
+ self.visited_nodes.add(node)
73
+ else:
74
+ return
75
+
76
+ for lib in node.linked_libraries:
77
+ self.visit(lib)
78
+
79
+ if isStale(lib):
80
+ self.markStale(node)
81
+
82
+ for src in node.sources:
83
+ self.visit(src)
84
+
85
+ if isStale(src):
86
+ self.markStale(node)
87
+
88
+
89
+ def visitSharedLibraryNode(self, node: SharedLibraryNode):
90
+
91
+ if node not in self.visited_nodes:
92
+ self.visited_nodes.add(node)
93
+ else:
94
+ return
95
+
96
+
97
+ for lib in node.linked_libraries:
98
+ self.visit(lib)
99
+
100
+ if isStale(lib):
101
+ self.markStale(node)
102
+
103
+ for src in node.sources:
104
+ self.visit(src)
105
+
106
+ if isStale(src):
107
+ self.markStale(node)
108
+
109
+
110
+ def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
111
+
112
+ if node not in self.visited_nodes:
113
+ self.visited_nodes.add(node)
114
+ else:
115
+ return
116
+
117
+
118
+
119
+ def visitSourceNode(self, node: SourceNode):
120
+
121
+ if node not in self.visited_nodes:
122
+ self.visited_nodes.add(node)
123
+ else:
124
+ return
125
+
126
+
127
+ if not node.outpath.exists():
128
+ self.markStale(node)
129
+ return
130
+
131
+ if node.outpath.stat().st_mtime < node.filepath.stat().st_mtime:
132
+ self.markStale(node)
133
+ return
134
+
135
+ self.current_source_outpath = node.outpath
136
+
137
+ for header in node.deps:
138
+ self.visit(header)
139
+
140
+ if isStale(header):
141
+ self.markStale(node)
142
+ return
143
+
144
+ self.current_source_outpath = None
145
+
146
+
147
+ def visitHeaderNode(self, node: HeaderNode):
148
+
149
+ if node not in self.visited_nodes:
150
+ self.visited_nodes.add(node)
151
+ else:
152
+ return
153
+
154
+ if self.current_source_outpath is None:
155
+ raise RuntimeError("Source path should not have been None. Unexpected.")
156
+
157
+ if self.current_source_outpath.stat().st_mtime < node.filepath.stat().st_mtime:
158
+ markStale(node)
159
+ return
160
+
161
+ for header in node.deps:
162
+ self.visit(header)
163
+
164
+ if isStale(header):
165
+ markStale(node)
166
+ return
167
+
File without changes
@@ -0,0 +1,175 @@
1
+ from src.zero.graph.nodes import *
2
+ from src.zero.graph.nodes import SharedLibraryNode
3
+ from src.zero.graph.visitor import NodeVisitor
4
+
5
+ from src.zero.compilers import BaseCompiler
6
+
7
+ from src.zero.orchestrator.config import BuildConfig
8
+ from src.zero.reporter import getReporter
9
+ from src.zero.analyzers.stale_detector import isStale
10
+
11
+
12
+ class Builder(NodeVisitor):
13
+
14
+ def __init__(self, config: BuildConfig) -> None:
15
+
16
+ super().__init__()
17
+
18
+ self.fresh_build = config.fresh_build
19
+
20
+ self.compiling_shared_lib = False
21
+
22
+ self.include_dirs: list[Path] = []
23
+ self.visited_nodes: set[object] = set()
24
+ self.current_target_arguments: list[str] = []
25
+
26
+ self.reporter = getReporter()
27
+
28
+ self.current_compiler: BaseCompiler
29
+
30
+
31
+ def detectStaleness(self, node: Node) -> bool:
32
+ if self.fresh_build:
33
+ return True
34
+ else:
35
+ return isStale(node)
36
+
37
+
38
+ def visitRootNode(self, node: RootNode):
39
+ self.reporter.startPhase("Compilation", "Compiling")
40
+
41
+ for target in node.targets:
42
+ self.current_compiler = node.target_compilers[target]
43
+ self.visit(target)
44
+
45
+ self.reporter.endPhase("Build complete")
46
+
47
+
48
+ def visitStaticLibraryNode(self, node: StaticLibraryNode):
49
+ if node in self.visited_nodes:
50
+ return
51
+
52
+ if not self.detectStaleness(node):
53
+ return
54
+
55
+ include_dirs = []
56
+
57
+ for lib in node.linked_libraries:
58
+ self.visit(lib)
59
+ include_dirs.extend(lib.public_headers)
60
+
61
+ include_dirs.extend(node.public_headers)
62
+ include_dirs.extend(node.private_headers)
63
+
64
+ self.include_dirs = include_dirs
65
+
66
+ self.current_target_arguments = node.arguments
67
+
68
+ for deps in node.sources:
69
+ self.visit(deps)
70
+
71
+ self.current_compiler.buildStaticLib([n.outpath for n in node.sources], node.libpath)
72
+
73
+ self.reporter.taskDone("Link ", f"{node.libpath.name}")
74
+
75
+ self.visited_nodes.add(node)
76
+
77
+
78
+ def visitSharedLibraryNode(self, node: SharedLibraryNode):
79
+ if node in self.visited_nodes:
80
+ return
81
+
82
+ if not self.detectStaleness(node):
83
+ return
84
+
85
+ self.compiling_shared_lib = True
86
+
87
+ include_dirs = []
88
+
89
+ for lib in node.linked_libraries:
90
+ self.visit(lib)
91
+ include_dirs.extend(lib.public_headers)
92
+
93
+ include_dirs.extend(node.public_headers)
94
+ include_dirs.extend(node.private_headers)
95
+
96
+ self.include_dirs = include_dirs
97
+
98
+ self.current_target_arguments = node.arguments
99
+
100
+ for deps in node.sources:
101
+ self.visit(deps)
102
+
103
+ self.current_compiler.buildSharedLib([n.outpath for n in node.sources], [l.libpath for l in node.linked_libraries], node.libpath)
104
+
105
+ self.compiling_shared_lib = False
106
+
107
+ self.reporter.taskDone("Link ", f"{node.libpath.name}")
108
+
109
+ self.visited_nodes.add(node)
110
+
111
+
112
+ def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
113
+
114
+ if not node.libpath.exists():
115
+ raise RuntimeError(f"Could not find pre-compiled library: {node.libpath}")
116
+
117
+ self.reporter.taskDone("Found", f"{node.libpath}")
118
+
119
+
120
+ def visitExecutableNode(self, node: ExecutableNode):
121
+ if node in self.visited_nodes:
122
+ return
123
+
124
+ if not self.detectStaleness(node):
125
+ return
126
+
127
+ include_dirs = []
128
+
129
+ for lib in node.linked_libraries:
130
+ self.visit(lib)
131
+ include_dirs.extend(lib.public_headers)
132
+
133
+ include_dirs.extend(node.private_headers)
134
+
135
+ self.include_dirs = include_dirs
136
+
137
+ self.current_target_arguments = node.arguments
138
+
139
+ for deps in node.sources:
140
+ self.visit(deps)
141
+
142
+ self.current_compiler.buildExecutable([n.outpath for n in node.sources], [n.libpath for n in node.linked_libraries], node.targetpath)
143
+
144
+ self.reporter.taskDone("Link ", f"{node.targetpath.name}")
145
+
146
+ self.visited_nodes.add(node)
147
+
148
+
149
+ def visitSourceNode(self, node: SourceNode):
150
+
151
+ if node in self.visited_nodes:
152
+ return
153
+
154
+ if not self.detectStaleness(node):
155
+ return
156
+
157
+ for deps in node.deps:
158
+ self.visit(deps)
159
+
160
+ self.current_compiler.buildFile(
161
+ node.filepath,
162
+ node.outpath,
163
+ for_shared=self.compiling_shared_lib,
164
+ include_dirs=self.include_dirs,
165
+ arguments=self.current_target_arguments
166
+ )
167
+
168
+ self.reporter.taskDone("Built", f"{node.filepath}")
169
+
170
+ self.visited_nodes.add(node)
171
+
172
+
173
+ def visitHeaderNode(self, node: HeaderNode):
174
+ for deps in node.deps:
175
+ self.visit(deps)
File without changes
@@ -0,0 +1,5 @@
1
+
2
+ from .main import main
3
+
4
+ if __name__ == "__main__":
5
+ main()