Skip to content

Instantly share code, notes, and snippets.

@hosaka
Created September 18, 2015 23:11
Show Gist options
  • Save hosaka/c0da35282f343e9dcf60 to your computer and use it in GitHub Desktop.
Save hosaka/c0da35282f343e9dcf60 to your computer and use it in GitHub Desktop.
Create workspace file for sublime text from template with options for ctags and cscope generation.
#! /usr/bin/env python
#
# TODO:
# - Separate ctags from sublime function
# - Predictable template location
# - Generate template if none found
# - Total size of cscope files
# - Custom exclude folders and more project flexebility for subl/cscope/ctags
# perhaps a file list with ignored folders used by all functions
# - Remove building functions, too project specific
#
# Script to setup the development workspace for the software project folder
# and assist with building the code. Read everything about the commands by
# using -h/--help flag.
#
# The script will prepare the dev environment for Sublime Text 3.
# All the folders you need to work on your software unit according to what is
# set in the template.sublime-project file will be included or excluded.
# Modify this file according to what you're working with
#
# This will create the *.sublime-project file and place it under the root
# directory of a created workspace. To make use of this feature you need:
# sublime text : http://www.sublimetext.com/
# template file : template.sublime-project
#
# The ctags database can also be built if ctags is installed excluding the
# unnecessary folders and placed in the root project folder to enable IDE-like
# Ctrl+Click definition navigation. To make use of the ctags generated file
# you need:
# ctags : http://ctags.sourceforge.net/
# sublime addon : https://github.com/SublimeText/CTags
#
# To make use of more advanced navigation, for example list callers, list
# calling functions and flexible symbol look up, a cscope file can be generated
# To use the scope files in your project you need:
# cscope : http://cscope.sourceforge.net/
# sublime addon : https://github.com/ameyp/CscopeSublime
#
############################ DEPRECATED FUNCTIONS #############################
# Remote building on windows via pstools is no longer supported
import os
import argparse
import subprocess
__author__ = "alex-march"
__version__ = "0.1.0"
APP_TITLE = "envmake"
APP_ROOT = os.path.realpath(os.path.dirname(__file__))
BUILD_SCRIPT_PATH = "/path/to/builddir"
BUILD_SCRIPT_FNAME = "build.py"
BUILD_LAST_FNAME = ".last_build"
class EnvBuilder():
# project directory, also acts as a project name
proj_dir = ""
# full path the project
proj_path = ""
def __init__(self):
self.args = self.parse_args()
# retain the given user path
self.proj_dir = self.args.project
# set the full build path
self.proj_path = os.path.join(os.getcwd(), self.proj_dir)
# check if project folder exists
if not os.path.isdir(self.proj_path):
print(APP_TITLE + ": " + self.proj_path + " is not a valid path")
return
# generate the sublime files and ctags if necessary
if self.args.action in "subl":
gen = WorkspaceGenerator()
gen.gen_sublime(self.proj_path, self.proj_dir, self.args.ctags)
# gen cscope
if self.args.cscope:
gen.gen_cscope(self.proj_dir)
# TODO: gen ctags outside of gen_sublime function
if self.args.ctags:
# gen.gen_ctags(self.proj_dir)
pass
# run build
else:
self.run_build()
def parse_args(self):
# set up the parser
parser = argparse.ArgumentParser(prog=APP_TITLE, description="available commands description")
subparser = parser.add_subparsers(title="available commands", help="build and create environment")
# project building
build_parser = subparser.add_parser("build", help="run the build system")
build_parser.add_argument("client", type=str, help="build client")
build_parser.add_argument("version", type=str, help="build bersion")
build_parser.add_argument("-k", "--keep_going", action="store_true", help="don't stop on error when building")
build_parser.add_argument("-j", "--jobs", type=int, help="run this amount of simultaneous jobs")
build_parser.add_argument("-n", "--no_last", action="store_true", help="do not store.last_build file")
# just run last build
subl_parser = subparser.add_parser("last", help="re-run the last build command in that folder")
subl_parser.set_defaults(action="last")
# envoronment setup
subl_parser = subparser.add_parser("subl", help="create sublime project files")
subl_parser.add_argument("-t", "--ctags", action="store_true", help="generate ctags index files")
subl_parser.add_argument("-s", "--cscope", action="store_true", help="generate cscope index files")
subl_parser.set_defaults(action="subl")
# common arguments
parser.add_argument("project", type=str, help="relative or full path to project")
return parser.parse_args()
def run_build(self):
# accumulate the build command
build_cmd = []
window_title = "building: {} version: {}"
# if this is a re-run of the last build, try to read the .last_build file
if self.args.action in "last":
# try to load the last build command
try:
with open(self.proj_path + "/" + BUILD_LAST_FNAME, "r") as lastfile:
# read only one line, ignore the rest of the file
build_cmd = lastfile.readline()
build_cmd = build_cmd.split(' ')
window_title = window_title.format(build_cmd[2], build_cmd[4])
except IOError:
# no last build file, run without one
print(APP_TITLE + ": no last build file, run new build to make one")
return
# this is a new build, make a new build command from the given arguments
else:
build_cmd = [BUILD_SCRIPT_FNAME, self.args.client, "-v", self.args.version]
if self.args.keep_going:
build_cmd.append("bparams=-k")
if self.args.jobs:
build_cmd.append("-j " + str(self.args.jobs))
window_title = window_title.format(self.args.client, self.args.version)
print(build_cmd)
# build directory
build_dir = "{}/{}".format(self.build_build_path, BUILD_SCRIPT_PATH)
# descend into the build/ms directory and launch the build_clinet.py
try:
print(APP_TITLE + ": building in " + build_dir)
# set title, subproc way doesn't work
os.system("title {}: {} path: {}".format(APP_TITLE, window_title, self.build_build_path))
# run the build command
subprocess.call(build_cmd, cwd=build_dir)
print(APP_TITLE + ": finished build")
except OSError:
print(APP_TITLE + ": build script not found")
finally:
# store last build command unless no_last flag has been set
if not self.args.no_last:
try:
with open(self.build_build_path + "/" + BUILD_LAST_FNAME, "w") as lastfile:
lastfile.write(" ".join(str(item) for item in build_cmd))
except IOError:
# no last build file
print(APP_TITLE + ": cannot write last build file, check permissions")
class WorkspaceGenerator():
# sublime project
subl_template = "template"
subl_proj_suf = ".sublime-project"
# ctags
ctags_exec = "ctags"
ctags_fname = ".tags"
# ctags_sorted_fname = ".tags_sorted_by_file"
ctags_exclude_dirs = []
# cscope
cscope_exec = "cscope"
cscope_fname = "cscope.out"
# cscope_include_dirs = []
def __init__(self):
pass
def gen_cscope (self, folder):
# cscope execute flags
# -R : recurse into directories
# -b : build the cross reference only
# -q : fast symbol lookup via inverted index (2 more files created)
# -k : kernel mode, turn off default include dir (/usr/include)
# -f : output file, defaults to cscope.out
cscope_cmd = [self.cscope_exec, "-R", "-b", "-q"]
# directories and outfile
cscope_dir = folder
cscope_outfile = os.path.join(cscope_dir, self.cscope_fname)
# stack include directories
# for fold in self.cscope_include_dirs:
# ctags_cmd.append("-I " + fold + " ")
# del fold
# launch cscope and save the scope file in the project directory
print(APP_TITLE + ": building cscope in project folder, be patient...")
try:
subprocess.Popen(cscope_cmd, cwd=cscope_dir).wait()
print(APP_TITLE + ": built in " + cscope_outfile + " ({} KB)".format(
os.path.getsize(cscope_outfile)/1024)
)
except OSError:
print(APP_TITLE + ": cscope executable not found, skipping...")
return
def gen_ctags(self, folder):
# ctags execute flags
# -R : recurse into directories
# -f : output filename
ctags_cmd = [self.ctags_exec, "-R", "-f " + self.ctags_fname]
ctags_dir = folder
ctags_outfile = os.path.join(ctags_dir, self.ctags_fname)
# stack all the exclude directories, just like in the sublime project
# if not self.ctags_exclude_dirs:
# for fold in self.ctags_exclude_dirs:
# ctags_cmd.append("--exclude=" + fold)
# del fold
# launch ctags and save the tags file in the project directory
print(APP_TITLE + ": building ctags in project folder, be patient...")
try:
subprocess.Popen(ctags_cmd, cwd=ctags_dir).wait()
print(APP_TITLE + ": built in " + ctags_outfile + " ({} KB)".format(
os.path.getsize(ctags_outfile)/1024)
)
except OSError:
print(APP_TITLE + ": ctags executable not found, skipping...")
return
def gen_sublime(self, folder, name, prepare_ctags):
# copy files to the project root
import json
# read the template file
try:
with open(
os.path.join(APP_ROOT, self.subl_template + self.subl_proj_suf)
) as infile:
# load template
proj_conf = json.load(infile)
except IOError:
# no template file
print(APP_TITLE + ": no template.sublime-project file, not making a new one")
return
# use the fields given on the command line
proj_conf["folders"][0]["name"] = name
proj_conf["folders"][0]["path"] = "."
# write the project file to the project path
outfile_name = os.path.join(folder, name + self.subl_proj_suf)
try:
with open(outfile_name, 'w') as outfile:
json.dump(proj_conf, outfile, sort_keys=True, indent=4)
except IOError:
print(APP_TITLE + ": cannot write project file in: " + outfile_name)
print(APP_TITLE + ": check permissions or project folder name")
return
print(APP_TITLE + ": created " + outfile_name)
# collect the exclude folders for ctags generation
if prepare_ctags:
for fold in proj_conf["folders"][0]["folder_exclude_patterns"]:
self.ctags_exclude_dirs.append(fold.encode("utf-8"))
del fold
# generate ctags
self.gen_ctags(folder)
if __name__ == "__main__":
builder = EnvBuilder()
{
"settings":
{
"tab_size": 4,
"translate_tabs_to_spaces": true,
"default_line_ending": "unix"
},
"folders":
[
{
"name": "",
"path": "",
"file_include_patterns": [
""
],
"file_exclude_patterns": [
".*",
"*.sublime*"
"cscope.*"
],
"folder_exclude_patterns":
[
""
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment