2014-09-24 00:55:54 +00:00
|
|
|
#!/usr/bin/env python
|
2014-12-18 18:50:47 +00:00
|
|
|
|
2016-02-11 19:48:58 +00:00
|
|
|
# Copyright (c) 2014-present, Facebook, Inc.
|
2014-12-18 18:50:47 +00:00
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# This source code is licensed under the BSD-style license found in the
|
2015-02-02 20:53:04 +00:00
|
|
|
# LICENSE file in the root directory of this source tree. An additional grant
|
2014-12-18 18:50:47 +00:00
|
|
|
# of patent rights can be found in the PATENTS file in the same directory.
|
2014-07-31 00:35:19 +00:00
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
import argparse
|
2014-07-31 00:35:19 +00:00
|
|
|
import ast
|
|
|
|
import jinja2
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
sys.path.append(SCRIPT_DIR + "/../tests")
|
2015-10-17 01:05:37 +00:00
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
from utils import platform
|
2014-07-31 00:35:19 +00:00
|
|
|
|
|
|
|
# the log format for the logging module
|
|
|
|
LOG_FORMAT = "%(levelname)s [Line %(lineno)d]: %(message)s"
|
|
|
|
|
2014-11-12 05:34:59 +00:00
|
|
|
# Read all implementation templates
|
|
|
|
TEMPLATES = {}
|
|
|
|
|
|
|
|
# Temporary reserved column names
|
2015-01-19 00:20:50 +00:00
|
|
|
RESERVED = ["n", "index"]
|
2014-07-31 00:35:19 +00:00
|
|
|
|
2015-05-07 04:58:23 +00:00
|
|
|
# Set the platform in osquery-language
|
2015-06-01 22:53:52 +00:00
|
|
|
PLATFORM = platform()
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2015-05-07 04:58:23 +00:00
|
|
|
# Supported SQL types for spec
|
2015-10-17 01:05:37 +00:00
|
|
|
|
|
|
|
|
2014-11-12 18:27:44 +00:00
|
|
|
class DataType(object):
|
|
|
|
def __init__(self, affinity, cpp_type="std::string"):
|
2014-11-13 06:33:27 +00:00
|
|
|
'''A column datatype is a pair of a SQL affinity to C++ type.'''
|
2014-11-12 18:27:44 +00:00
|
|
|
self.affinity = affinity
|
|
|
|
self.type = cpp_type
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-12 18:27:44 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return self.affinity
|
|
|
|
|
2014-11-13 06:33:27 +00:00
|
|
|
# Define column-type MACROs for the table specs
|
2015-11-09 09:19:45 +00:00
|
|
|
TEXT = DataType("TEXT_TYPE")
|
|
|
|
DATE = DataType("TEXT_TYPE")
|
|
|
|
DATETIME = DataType("TEXT_TYPE")
|
|
|
|
INTEGER = DataType("INTEGER_TYPE", "int")
|
|
|
|
BIGINT = DataType("BIGINT_TYPE", "long long int")
|
|
|
|
UNSIGNED_BIGINT = DataType("UNSIGNED_BIGINT_TYPE", "long long unsigned int")
|
|
|
|
DOUBLE = DataType("DOUBLE_TYPE", "double")
|
|
|
|
BLOB = DataType("BLOB_TYPE", "Blob")
|
2014-11-12 18:27:44 +00:00
|
|
|
|
2015-03-19 03:47:35 +00:00
|
|
|
# Define table-category MACROS from the table specs
|
|
|
|
UNKNOWN = "UNKNOWN"
|
|
|
|
UTILITY = "UTILITY"
|
|
|
|
SYSTEM = "SYSTEM"
|
|
|
|
NETWORK = "NETWORK"
|
|
|
|
EVENTS = "EVENTS"
|
|
|
|
APPLICATION = "APPLICATION"
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2016-05-18 19:23:52 +00:00
|
|
|
# This should mimic the C++ enumeration ColumnOptions in table.h
|
|
|
|
COLUMN_OPTIONS = {
|
|
|
|
"index": "INDEX",
|
|
|
|
"additional": "ADDITIONAL",
|
|
|
|
"required": "REQUIRED",
|
|
|
|
"optimized": "OPTIMIZED",
|
|
|
|
}
|
|
|
|
|
|
|
|
# Column options that render tables uncacheable.
|
|
|
|
NON_CACHEABLE = [
|
|
|
|
"REQUIRED",
|
|
|
|
"ADDITIONAL",
|
|
|
|
"OPTIMIZED",
|
|
|
|
]
|
|
|
|
|
2015-10-17 01:05:37 +00:00
|
|
|
|
2014-08-07 20:50:40 +00:00
|
|
|
def to_camel_case(snake_case):
|
|
|
|
""" convert a snake_case string to camelCase """
|
|
|
|
components = snake_case.split('_')
|
|
|
|
return components[0] + "".join(x.title() for x in components[1:])
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-10 20:28:08 +00:00
|
|
|
def lightred(msg):
|
|
|
|
return "\033[1;31m %s \033[0m" % str(msg)
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-13 06:33:27 +00:00
|
|
|
def is_blacklisted(table_name, path=None, blacklist=None):
|
2014-11-12 05:34:59 +00:00
|
|
|
"""Allow blacklisting by tablename."""
|
2014-11-13 06:33:27 +00:00
|
|
|
if blacklist is None:
|
2015-06-01 22:53:52 +00:00
|
|
|
specs_path = os.path.dirname(path)
|
|
|
|
if os.path.basename(specs_path) != "specs":
|
2016-07-31 18:32:31 +00:00
|
|
|
specs_path = os.path.dirname(specs_path)
|
2014-11-13 06:33:27 +00:00
|
|
|
blacklist_path = os.path.join(specs_path, "blacklist")
|
|
|
|
if not os.path.exists(blacklist_path):
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
with open(blacklist_path, "r") as fh:
|
2014-11-14 23:04:19 +00:00
|
|
|
blacklist = [
|
|
|
|
line.strip() for line in fh.read().split("\n")
|
|
|
|
if len(line.strip()) > 0 and line.strip()[0] != "#"
|
|
|
|
]
|
2014-11-13 06:33:27 +00:00
|
|
|
except:
|
|
|
|
# Blacklist is not readable.
|
|
|
|
return False
|
2015-05-07 04:58:23 +00:00
|
|
|
if not blacklist:
|
|
|
|
return False
|
2014-11-12 05:34:59 +00:00
|
|
|
|
2015-05-07 04:58:23 +00:00
|
|
|
# table_name based blacklisting!
|
|
|
|
for item in blacklist:
|
|
|
|
item = item.split(":")
|
|
|
|
# If this item is restricted to a platform and the platform
|
|
|
|
# and table name match
|
|
|
|
if len(item) > 1 and PLATFORM == item[0] and table_name == item[1]:
|
|
|
|
return True
|
|
|
|
elif len(item) == 1 and table_name == item[0]:
|
|
|
|
return True
|
|
|
|
return False
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
def setup_templates(templates_path):
|
2014-11-12 05:34:59 +00:00
|
|
|
if not os.path.exists(templates_path):
|
2015-10-17 01:05:37 +00:00
|
|
|
templates_path = os.path.join(
|
|
|
|
os.path.dirname(tables_path), "templates")
|
2015-04-16 00:30:16 +00:00
|
|
|
if not os.path.exists(templates_path):
|
2015-10-17 01:05:37 +00:00
|
|
|
print("Cannot read templates path: %s" % (templates_path))
|
2015-04-16 00:30:16 +00:00
|
|
|
exit(1)
|
|
|
|
for template in os.listdir(templates_path):
|
2014-11-12 05:34:59 +00:00
|
|
|
template_name = template.split(".", 1)[0]
|
2015-10-17 01:05:37 +00:00
|
|
|
with open(os.path.join(templates_path, template), "r") as fh:
|
2014-11-12 05:34:59 +00:00
|
|
|
TEMPLATES[template_name] = fh.read().replace("\\\n", "")
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
class Singleton(object):
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
"""
|
|
|
|
Make sure that anything that subclasses Singleton can only be instantiated
|
|
|
|
once
|
|
|
|
"""
|
|
|
|
|
|
|
|
_instance = None
|
|
|
|
|
|
|
|
def __new__(self, *args, **kwargs):
|
|
|
|
if not self._instance:
|
|
|
|
self._instance = super(Singleton, self).__new__(
|
|
|
|
self, *args, **kwargs)
|
|
|
|
return self._instance
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
class TableState(Singleton):
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
"""
|
|
|
|
Maintain the state of of the table commands during the execution of
|
|
|
|
the config file
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.table_name = ""
|
|
|
|
self.schema = []
|
|
|
|
self.header = ""
|
|
|
|
self.impl = ""
|
|
|
|
self.function = ""
|
2014-09-25 17:17:32 +00:00
|
|
|
self.class_name = ""
|
2014-11-07 01:12:40 +00:00
|
|
|
self.description = ""
|
2015-02-09 01:40:35 +00:00
|
|
|
self.attributes = {}
|
2015-06-01 22:53:52 +00:00
|
|
|
self.examples = []
|
2016-05-19 16:40:43 +00:00
|
|
|
self.aliases = []
|
2016-05-18 19:23:52 +00:00
|
|
|
self.has_options = False
|
2016-05-19 16:40:43 +00:00
|
|
|
self.has_column_aliases = False
|
2014-11-07 01:12:40 +00:00
|
|
|
|
|
|
|
def columns(self):
|
2014-11-10 20:28:08 +00:00
|
|
|
return [i for i in self.schema if isinstance(i, Column)]
|
2014-11-07 01:12:40 +00:00
|
|
|
|
|
|
|
def foreign_keys(self):
|
2014-11-10 20:28:08 +00:00
|
|
|
return [i for i in self.schema if isinstance(i, ForeignKey)]
|
2014-07-31 00:35:19 +00:00
|
|
|
|
2014-11-12 05:34:59 +00:00
|
|
|
def generate(self, path, template="default"):
|
2014-07-31 00:35:19 +00:00
|
|
|
"""Generate the virtual table files"""
|
|
|
|
logging.debug("TableState.generate")
|
|
|
|
|
2016-05-18 19:23:52 +00:00
|
|
|
all_options = []
|
|
|
|
# Create a list of column options from the kwargs passed to the column.
|
2015-11-09 02:31:50 +00:00
|
|
|
for column in self.columns():
|
2016-05-18 19:23:52 +00:00
|
|
|
column_options = []
|
|
|
|
for option in column.options:
|
|
|
|
# Only allow explicitly-defined options.
|
|
|
|
if option in COLUMN_OPTIONS:
|
|
|
|
column_options.append(COLUMN_OPTIONS[option])
|
|
|
|
all_options.append(COLUMN_OPTIONS[option])
|
|
|
|
column.options_set = " | ".join(column_options)
|
2016-05-19 16:40:43 +00:00
|
|
|
if len(column.aliases) > 0:
|
|
|
|
self.has_column_aliases = True
|
2016-05-18 19:23:52 +00:00
|
|
|
if len(all_options) > 0:
|
|
|
|
self.has_options = True
|
|
|
|
if "cacheable" in self.attributes:
|
|
|
|
if len(set(all_options).intersection(NON_CACHEABLE)) > 0:
|
|
|
|
print(lightred("Table cannot be marked cacheable: %s" % (path)))
|
2015-11-09 02:31:50 +00:00
|
|
|
exit(1)
|
2015-04-16 00:30:16 +00:00
|
|
|
if self.table_name == "" or self.function == "":
|
2015-11-09 02:31:50 +00:00
|
|
|
print(lightred("Invalid table spec: %s" % (path)))
|
2015-04-16 00:30:16 +00:00
|
|
|
exit(1)
|
|
|
|
|
2014-11-12 05:34:59 +00:00
|
|
|
# Check for reserved column names
|
|
|
|
for column in self.columns():
|
|
|
|
if column.name in RESERVED:
|
2015-10-17 01:05:37 +00:00
|
|
|
print(lightred(("Cannot use column name: %s in table: %s "
|
|
|
|
"(the column name is reserved)" % (
|
|
|
|
column.name, self.table_name))))
|
2014-11-12 05:34:59 +00:00
|
|
|
exit(1)
|
|
|
|
|
2014-09-24 08:58:12 +00:00
|
|
|
path_bits = path.split("/")
|
|
|
|
for i in range(1, len(path_bits)):
|
|
|
|
dir_path = ""
|
|
|
|
for j in range(i):
|
|
|
|
dir_path += "%s/" % path_bits[j]
|
|
|
|
if not os.path.exists(dir_path):
|
2015-01-06 02:11:10 +00:00
|
|
|
try:
|
|
|
|
os.mkdir(dir_path)
|
|
|
|
except:
|
|
|
|
# May encounter a race when using a make jobserver.
|
|
|
|
pass
|
2014-09-24 08:58:12 +00:00
|
|
|
logging.debug("generating %s" % path)
|
2016-05-18 19:23:52 +00:00
|
|
|
self.impl_content = jinja2.Template(TEMPLATES[template]).render(
|
|
|
|
table_name=self.table_name,
|
|
|
|
table_name_cc=to_camel_case(self.table_name),
|
|
|
|
schema=self.columns(),
|
|
|
|
header=self.header,
|
|
|
|
impl=self.impl,
|
|
|
|
function=self.function,
|
|
|
|
class_name=self.class_name,
|
|
|
|
attributes=self.attributes,
|
|
|
|
examples=self.examples,
|
2016-05-19 16:40:43 +00:00
|
|
|
aliases=self.aliases,
|
2016-05-18 19:23:52 +00:00
|
|
|
has_options=self.has_options,
|
2016-05-19 16:40:43 +00:00
|
|
|
has_column_aliases=self.has_column_aliases,
|
2016-05-18 19:23:52 +00:00
|
|
|
)
|
|
|
|
|
2014-09-24 08:58:12 +00:00
|
|
|
with open(path, "w+") as file_h:
|
2014-07-31 00:35:19 +00:00
|
|
|
file_h.write(self.impl_content)
|
|
|
|
|
2014-11-10 20:28:08 +00:00
|
|
|
def blacklist(self, path):
|
2015-10-17 01:05:37 +00:00
|
|
|
print(lightred("Blacklisting generated %s" % path))
|
2014-11-10 20:28:08 +00:00
|
|
|
logging.debug("blacklisting %s" % path)
|
2014-11-12 05:34:59 +00:00
|
|
|
self.generate(path, template="blacklist")
|
2014-11-10 20:28:08 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
table = TableState()
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
class Column(object):
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
"""
|
2014-11-07 01:12:40 +00:00
|
|
|
Part of an osquery table schema.
|
|
|
|
Define a column by name and type with an optional description to assist
|
|
|
|
documentation generation and reference.
|
2014-07-31 00:35:19 +00:00
|
|
|
"""
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2016-05-19 16:40:43 +00:00
|
|
|
def __init__(self, name, col_type, description="", aliases=[], **kwargs):
|
2014-11-12 18:27:44 +00:00
|
|
|
self.name = name
|
|
|
|
self.type = col_type
|
|
|
|
self.description = description
|
2016-05-19 16:40:43 +00:00
|
|
|
self.aliases = aliases
|
2015-06-05 18:20:24 +00:00
|
|
|
self.options = kwargs
|
2014-11-07 01:12:40 +00:00
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-07 01:12:40 +00:00
|
|
|
class ForeignKey(object):
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-10 20:28:08 +00:00
|
|
|
"""
|
|
|
|
Part of an osquery table schema.
|
|
|
|
Loosely define a column in a table spec as a Foreign key in another table.
|
|
|
|
"""
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-11-10 20:28:08 +00:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
self.column = kwargs.get("column", "")
|
|
|
|
self.table = kwargs.get("table", "")
|
2014-07-31 00:35:19 +00:00
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2016-05-19 16:40:43 +00:00
|
|
|
def table_name(name, aliases=[]):
|
2014-07-31 00:35:19 +00:00
|
|
|
"""define the virtual table name"""
|
|
|
|
logging.debug("- table_name")
|
|
|
|
logging.debug(" - called with: %s" % name)
|
|
|
|
table.table_name = name
|
2014-11-11 16:35:25 +00:00
|
|
|
table.description = ""
|
2015-02-09 01:40:35 +00:00
|
|
|
table.attributes = {}
|
2015-06-01 22:53:52 +00:00
|
|
|
table.examples = []
|
2016-05-19 16:40:43 +00:00
|
|
|
table.aliases = aliases
|
2014-07-31 00:35:19 +00:00
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
def schema(schema_list):
|
|
|
|
"""
|
|
|
|
define a list of Column object which represent the columns of your virtual
|
|
|
|
table
|
|
|
|
"""
|
|
|
|
logging.debug("- schema")
|
2014-11-07 01:12:40 +00:00
|
|
|
for it in schema_list:
|
|
|
|
if isinstance(it, Column):
|
2014-11-10 20:28:08 +00:00
|
|
|
logging.debug(" - column: %s (%s)" % (it.name, it.type))
|
2014-11-07 01:12:40 +00:00
|
|
|
if isinstance(it, ForeignKey):
|
2014-11-10 20:28:08 +00:00
|
|
|
logging.debug(" - foreign_key: %s (%s)" % (it.column, it.table))
|
2014-07-31 00:35:19 +00:00
|
|
|
table.schema = schema_list
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2015-02-09 01:40:35 +00:00
|
|
|
def description(text):
|
|
|
|
table.description = text
|
|
|
|
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
def select_all(name=None):
|
2015-10-17 01:05:37 +00:00
|
|
|
if name is None:
|
2015-06-01 22:53:52 +00:00
|
|
|
name = table.table_name
|
|
|
|
return "select count(*) from %s;" % (name)
|
|
|
|
|
|
|
|
|
|
|
|
def examples(example_queries):
|
|
|
|
table.examples = example_queries
|
|
|
|
|
|
|
|
|
2015-05-12 06:31:13 +00:00
|
|
|
def attributes(**kwargs):
|
2015-02-09 01:40:35 +00:00
|
|
|
for attr in kwargs:
|
|
|
|
table.attributes[attr] = kwargs[attr]
|
|
|
|
|
|
|
|
|
2014-07-31 00:35:19 +00:00
|
|
|
def implementation(impl_string):
|
|
|
|
"""
|
|
|
|
define the path to the implementation file and the function which
|
|
|
|
implements the virtual table. You should use the following format:
|
|
|
|
|
|
|
|
# the path is "osquery/table/implementations/foo.cpp"
|
|
|
|
# the function is "QueryData genFoo();"
|
2014-10-14 04:58:26 +00:00
|
|
|
implementation("foo@genFoo")
|
2014-07-31 00:35:19 +00:00
|
|
|
"""
|
|
|
|
logging.debug("- implementation")
|
2014-10-14 04:58:26 +00:00
|
|
|
filename, function = impl_string.split("@")
|
2014-09-25 17:17:32 +00:00
|
|
|
class_parts = function.split("::")[::-1]
|
|
|
|
function = class_parts[0]
|
|
|
|
class_name = class_parts[1] if len(class_parts) > 1 else ""
|
2014-10-14 04:58:26 +00:00
|
|
|
impl = "%s.cpp" % filename
|
2014-07-31 00:35:19 +00:00
|
|
|
logging.debug(" - impl => %s" % impl)
|
|
|
|
logging.debug(" - function => %s" % function)
|
2014-09-25 17:17:32 +00:00
|
|
|
logging.debug(" - class_name => %s" % class_name)
|
2014-07-31 00:35:19 +00:00
|
|
|
table.impl = impl
|
|
|
|
table.function = function
|
2014-09-25 17:17:32 +00:00
|
|
|
table.class_name = class_name
|
2014-07-31 00:35:19 +00:00
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
'''Check if the table has a subscriber attribute, if so, enforce time.'''
|
|
|
|
if "event_subscriber" in table.attributes:
|
|
|
|
columns = {}
|
|
|
|
# There is no dictionary comprehension on all supported platforms.
|
|
|
|
for column in table.schema:
|
|
|
|
if isinstance(column, Column):
|
|
|
|
columns[column.name] = column.type
|
|
|
|
if "time" not in columns:
|
|
|
|
print(lightred("Event subscriber: %s needs a 'time' column." % (
|
|
|
|
table.table_name)))
|
|
|
|
sys.exit(1)
|
|
|
|
if columns["time"] is not BIGINT:
|
|
|
|
print(lightred(
|
|
|
|
"Event subscriber: %s, 'time' column must be a %s type" % (
|
|
|
|
table.table_name, BIGINT)))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2014-11-14 23:04:19 +00:00
|
|
|
|
2014-08-19 08:26:51 +00:00
|
|
|
def main(argc, argv):
|
2015-10-17 01:05:37 +00:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
"Generate C++ Table Plugin from specfile.")
|
2015-06-01 22:53:52 +00:00
|
|
|
parser.add_argument(
|
|
|
|
"--debug", default=False, action="store_true",
|
|
|
|
help="Output debug messages (when developing)"
|
|
|
|
)
|
2016-07-31 18:32:31 +00:00
|
|
|
parser.add_argument("--disable-blacklist", default=False,
|
|
|
|
action="store_true")
|
2016-04-11 16:33:45 +00:00
|
|
|
parser.add_argument("--foreign", default=False, action="store_true",
|
|
|
|
help="Generate a foreign table")
|
2015-06-01 22:53:52 +00:00
|
|
|
parser.add_argument("--templates", default=SCRIPT_DIR + "/templates",
|
2015-10-17 01:05:37 +00:00
|
|
|
help="Path to codegen output .cpp.in templates")
|
2015-06-01 22:53:52 +00:00
|
|
|
parser.add_argument("spec_file", help="Path to input .table spec file")
|
|
|
|
parser.add_argument("output", help="Path to output .cpp file")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
if args.debug:
|
2014-07-31 00:35:19 +00:00
|
|
|
logging.basicConfig(format=LOG_FORMAT, level=logging.DEBUG)
|
|
|
|
else:
|
|
|
|
logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
filename = args.spec_file
|
|
|
|
output = args.output
|
2015-02-02 20:53:04 +00:00
|
|
|
if filename.endswith(".table"):
|
|
|
|
# Adding a 3rd parameter will enable the blacklist
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
setup_templates(args.templates)
|
2015-02-02 20:53:04 +00:00
|
|
|
with open(filename, "rU") as file_handle:
|
|
|
|
tree = ast.parse(file_handle.read())
|
|
|
|
exec(compile(tree, "<string>", "exec"))
|
|
|
|
blacklisted = is_blacklisted(table.table_name, path=filename)
|
2016-07-31 18:32:31 +00:00
|
|
|
if not args.disable_blacklist and blacklisted:
|
2015-02-02 20:53:04 +00:00
|
|
|
table.blacklist(output)
|
|
|
|
else:
|
2016-04-11 16:33:45 +00:00
|
|
|
template_type = "default" if not args.foreign else "foreign"
|
|
|
|
table.generate(output, template=template_type)
|
2014-07-31 00:35:19 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2015-06-01 22:53:52 +00:00
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
2014-08-19 08:26:51 +00:00
|
|
|
main(len(sys.argv), sys.argv)
|