-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assignment 2.1: Build Automation Tool #60
Changes from 6 commits
ca5e40c
a338501
42557d3
1f28c4f
f33e3f7
6270d50
f0be0a3
1e5bd0d
cd94bc8
9bcb8cb
c633b29
ac947b8
d71d9c4
277b7d6
f1c42c3
5f26c51
22580e3
a1b25bd
c0da0bc
166c5db
923ead1
5da1c4c
980e9e1
c421aa6
eabddd1
7df0d4a
fc505ed
9c83a4b
4ba29bc
0cf3e73
54a3e62
31226cc
28a1c63
60c414e
4e1cadf
86c116c
cfae026
b77e20b
3658eb8
ce7515e
2ec874f
64f9272
18f8219
13b446d
baf6e64
f574672
4f9bdfe
d9f55eb
1176bfa
1f9d18b
f516ae0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/venv/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import json | ||
|
||
|
||
class BuildConfig: | ||
"""Parses and Stores build.config""" | ||
def __init__(self, json_path): | ||
# Parse JSON | ||
json_file = open(json_path) | ||
self._raw_json = json.load(json_file) | ||
json_file.close() | ||
|
||
# Validate the config file | ||
self.validate() | ||
|
||
self._command = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Proper naming is important:
|
||
for command in self._raw_json: | ||
self._command[command['name']] = command | ||
|
||
def command_names(self): | ||
"""Returns list of Commands""" | ||
return list(self._command) | ||
|
||
def deps(self, command_name): | ||
"""Return List of Dependencies""" | ||
return self._command[command_name]['deps'] | ||
|
||
def command(self, command_name): | ||
"""Returns Command String""" | ||
return self._command[command_name]['command'] | ||
|
||
def files(self, command_name): | ||
"""Returns List of Associated Files""" | ||
return self._command[command_name]['files'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create a |
||
|
||
def validate(self): | ||
# TODO: Think about cases and Implement this | ||
pass | ||
|
||
|
||
if __name__ == "__main__": | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import subprocess | ||
|
||
|
||
def run(command_string, cwd, print_command): | ||
"""Run Command and Return Exit Code. Optionally Print the command itself""" | ||
if print_command: | ||
print(command_string) | ||
return subprocess.run(command_string, shell=True, cwd=cwd).returncode | ||
|
||
|
||
if __name__ == '__main__': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This section does nothing. Remove it. |
||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from Builder.lib.commandrunner import run | ||
from Builder.lib.buildconfig import BuildConfig | ||
import sys | ||
import os | ||
|
||
|
||
def execute(command, containing_folder_path): | ||
"""Parses JSON, Resolves Dependencies and Executes Command""" | ||
|
||
# Parse build.json in current directory | ||
config = BuildConfig(containing_folder_path + "/build.json") | ||
|
||
# Parse Dependencies First | ||
try: | ||
deps = config.deps(command) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if I wanted to run "build algorithms/sort_bubble"? |
||
dep_count = len(deps) | ||
print("Executing {} dependencies for {} in {}...".format(dep_count, command, containing_folder_path)) | ||
for dep in deps: | ||
try: | ||
# Command in Child Folder | ||
dep_containing_folder_path, dep_command = dep.rsplit('/', 1) | ||
dep_containing_folder_path = containing_folder_path + "/" + dep_containing_folder_path | ||
execute(dep_command, dep_containing_folder_path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to add checks for circular dependencies. |
||
except ValueError: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ValueError can be generated by so many things that I have no idea when this will happen. Maybe you want to use a more specific exception? |
||
# Command in Same Folder | ||
execute(dep, containing_folder_path) | ||
except KeyError: | ||
# No Dependencies | ||
print("No dependencies found for {} in {}...".format(command, containing_folder_path)) | ||
|
||
# Execute Command after processing dependencies | ||
print("Executing {} in {}".format(command, containing_folder_path)) | ||
return_value = run(config.command(command), containing_folder_path, print_command=True) | ||
|
||
# Stop Execution if Command Fails | ||
if return_value != 0: | ||
exit(-1) | ||
|
||
|
||
if __name__ == '__main__': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might still be useful to keep this main application logic in a separate file from your library. |
||
command = sys.argv[1] | ||
execute(command, os.getcwd()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[ | ||
{ | ||
"name": "clean", | ||
"command": "rm -f *.o" | ||
}, | ||
{ | ||
"name": "sort_bubble", | ||
"files": ["sort_bubble.cpp"], | ||
"command": "g++ -c sort_bubble.cpp" | ||
}, | ||
{ | ||
"name": "sort_merge", | ||
"files": ["sort_merge.cpp"], | ||
"command": "g++ -c sort_merge.cpp" | ||
}, | ||
{ | ||
"name": "sort_quick", | ||
"files": ["sort_quick.cpp"], | ||
"command": "g++ -c sort_quick.cpp" | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#include<vector> | ||
#include<algorithm> | ||
|
||
void sort_bubble(std::vector<int> &arr) { | ||
sort(arr.begin(), arr.end()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#include<vector> | ||
#include<algorithm> | ||
|
||
void sort_merge(std::vector<int> &arr) { | ||
sort(arr.begin(), arr.end()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#include<vector> | ||
#include<algorithm> | ||
|
||
void sort_quick(std::vector<int> &arr) { | ||
sort(arr.begin(), arr.end()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[ | ||
{ | ||
"name": "clean", | ||
"deps": ["algorithms/clean"], | ||
"command": "rm -f test.o && rm -f test.out" | ||
}, | ||
{ | ||
"name": "test", | ||
"files": ["test.cpp"], | ||
"command": "g++ -std=c++11 -c test.cpp" | ||
}, | ||
{ | ||
"name": "run", | ||
"deps": ["test", "algorithms/sort_bubble", "algorithms/sort_merge", "algorithms/sort_quick"], | ||
"command": "g++ algorithms/sort_bubble.o algorithms/sort_merge.o algorithms/sort_quick.o test.o -o test.out && ./test.out" | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#include<iostream> | ||
#include<vector> | ||
|
||
void sort_quick(std::vector<int>&); | ||
void sort_merge(std::vector<int>&); | ||
void sort_bubble(std::vector<int>&); | ||
|
||
void print(const std::vector<int> &vec) { | ||
for(auto &x: vec) { | ||
std::cout << x << " "; | ||
} | ||
std::cout << "\n"; | ||
} | ||
|
||
int main() { | ||
std::vector<int> a = {5, 4, 3, 2, 1}, b = a, c = a; | ||
sort_quick(a); | ||
sort_merge(b); | ||
sort_bubble(c); | ||
print(a); | ||
print(b); | ||
print(c); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[ | ||
{ | ||
"name": "clean", | ||
"deps": ["algorithms/clean"], | ||
"command": "rm -f test.o && rm -f test.exe" | ||
}, | ||
{ | ||
"name": "test", | ||
"files": ["test.cpp"], | ||
"command": "g++ -std=c++11 -c test.cpp" | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import unittest | ||
from Builder.lib.buildconfig import BuildConfig | ||
|
||
|
||
class TestJsonParser(unittest.TestCase): | ||
def test_sample_json_parser_correctly(self): | ||
json_path = 'json/test1.json' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The input to your BuildConfig class should be a directory, not a specific file. You can add the "build.json" suffix in the BuildConfig class, instead of relying on the caller to add it. |
||
config = BuildConfig(json_path) | ||
self.assertEqual(config.command_names(), ['clean', 'test']) | ||
self.assertEqual(config.deps('clean'), ["algorithms/clean"]) | ||
self.assertEqual(config.command('clean'), "rm -f test.o && rm -f test.exe") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting, these utility methods can definitely be useful, but I'd recommend better naming here. |
||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import unittest | ||
import subprocess | ||
from Builder.main import execute | ||
|
||
|
||
class TestBuilder(unittest.TestCase): | ||
def test_compilation(self): | ||
path = '/home/ankurdubey/Code/Github/project-omega/solutions' \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This path is non-transferrable. If I downloaded this code in my machine, this test would fail, which is bad. |
||
'/ankurdubey521/Build Automation Tool/Builder/tests/filetree' | ||
execute('run', path) | ||
exec_path = '"' + path + '/test.out' + '"' | ||
result = subprocess.run(exec_path, shell=True, capture_output=True, text=True) | ||
self.assertEqual(result.stdout, '1 2 3 4 5 \n1 2 3 4 5 \n1 2 3 4 5 \n') | ||
execute('clean', path) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import unittest | ||
from Builder.lib.commandrunner import run | ||
|
||
|
||
class TestCommandRunner(unittest.TestCase): | ||
def test_basic_command(self): | ||
command = "echo 'Hello World!'" | ||
exit_code = run(command, print_command=False, cwd='/') | ||
self.assertEqual(0, exit_code) | ||
|
||
def test_nonzero_exit_code(self): | ||
command = "exit 1" | ||
exit_code = run(command, print_command=False, cwd='/') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This should be a default argument value. |
||
self.assertEqual(1, exit_code) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() | ||
kaustubh-karkare marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should use something like: