uberpoet/projectgen.py (100 lines of code) (raw):

# Copyright (c) 2018 Uber Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import absolute_import import logging import shutil import tempfile from os.path import dirname, join from .commandlineutil import count_loc from .filegen import SwiftFileGenerator from .moduletree import ModuleNode from .util import first_in_dict, first_key, makedir class BuckProjectGenerator(object): DIR_NAME = dirname(__file__) RESOURCE_DIR = join(DIR_NAME, "resources") def __init__(self, app_root, buck_app_root, use_wmo=False): self.app_root = app_root self.buck_app_root = buck_app_root self.bzl_lib_template = self.load_resource("mocklibtemplate.bzl") self.bzl_app_template = self.load_resource("mockapptemplate.bzl") self.swift_gen = SwiftFileGenerator() self.use_wmo = use_wmo self.swift_file_size_loc = None self.calculate_loc() @property def wmo_state(self): return "YES" if self.use_wmo else "NO" def calculate_loc(self): # actual code = lines of code, minus whitespace # calculated using cloc file_result = self.swift_gen.gen_file(3, 3) tmp_file_path = join(tempfile.gettempdir(), 'ub_mock_gen_example_file.swift') with open(tmp_file_path, 'w') as f: f.write(file_result.text) self.swift_file_size_loc = count_loc(tmp_file_path) if self.swift_file_size_loc == -1: logging.warning("Using fallback loc calc method due to cloc not being installed.") # fallback if cloc is not installed # this fallback is based on running cloc on the file made by `self.swift_gen.gen_file(3, 3)` # and saving the result of cloc(file_result.text) / file_result.text_line_count to here: fallback_code_multiplier = 0.811537333 self.swift_file_size_loc = int(file_result.text_line_count * fallback_code_multiplier) @staticmethod def load_resource(name): with open(join(BuckProjectGenerator.RESOURCE_DIR, name), "r") as f: return f.read() @staticmethod def copy_resource(name, dest): origin = join(BuckProjectGenerator.RESOURCE_DIR, name) shutil.copyfile(origin, dest) @staticmethod def write_file(path, text): with open(path, "w") as f: f.write(text) @staticmethod def make_list_str(items): return (",\n" + (" " * 8)).join(items) def make_dep_list(self, items): return self.make_list_str(["'/{0}/{1}:{1}'".format(self.buck_app_root, i) for i in items]) def make_scheme_list(self, items): return self.make_list_str( ["{2: <20} :'/{0}/{1}:{1}Scheme'".format(self.buck_app_root, i, "'{}'".format(i)) for i in items]) # Generation Functions def gen_app(self, app_node, node_list, target_loc): library_node_list = [n for n in node_list if n.node_type == ModuleNode.LIBRARY] total_code_units = 0 for l in library_node_list: total_code_units += l.code_units loc_per_unit = target_loc / total_code_units module_index = {n.name: self.gen_lib_module(n, loc_per_unit) for n in library_node_list} app_module_dir = join(self.app_root, "App") makedir(app_module_dir) app_files = { "main.swift": self.gen_app_main(app_node, module_index), "BUCK": self.gen_app_buck(app_node, library_node_list), } self.copy_resource("Info.plist", join(app_module_dir, "Info.plist")) for name, text in app_files.iteritems(): self.write_file(join(app_module_dir, name), text) def gen_app_buck(self, node, all_nodes): module_dep_list = self.make_dep_list([i.name for i in node.deps]) module_scheme_list = self.make_scheme_list([i.name for i in all_nodes]) return self.bzl_app_template.format(module_scheme_list, module_dep_list, self.wmo_state) def gen_app_main(self, app_node, module_index): importing_module_name = app_node.deps[0].name file_index = first_in_dict(module_index[importing_module_name]) class_key = first_key(file_index.classes) class_index = first_in_dict(file_index.classes) function_key = class_index[0] return self.swift_gen.gen_main(importing_module_name, class_key, function_key) # Library Generation def gen_lib_module(self, module_node, loc_per_unit): # Make BUCK Text deps = self.make_dep_list([i.name for i in module_node.deps]) buck_text = self.bzl_lib_template.format(module_node.name, deps, self.wmo_state) # Make Swift Text file_count = (loc_per_unit * module_node.code_units) / self.swift_file_size_loc if file_count < 1: raise ValueError("Lines of code count is too small for the module to fit one file, increase it.") files = {"File{}.swift".format(i): self.swift_gen.gen_file(3, 3) for i in xrange(file_count)} # Make Module Directories module_dir_path = join(self.app_root, module_node.name) files_dir_path = join(module_dir_path, "Sources") makedir(module_dir_path) makedir(files_dir_path) # Write BUCK File buck_path = join(module_dir_path, "BUCK") self.write_file(buck_path, buck_text) # Write Swift Files for file_name, file_obj in files.iteritems(): file_path = join(files_dir_path, file_name) self.write_file(file_path, file_obj.text) file_obj.text = "" # Save memory after write module_node.extra_info = files return files