Skip to content

Commit

Permalink
__file__ in configs, multi-config, vscode fixes
Browse files Browse the repository at this point in the history
* Python config files now have `__file__` set in the global namespace
* Add parse support for `BYPRODUCTS` in `add_custom_command`
* Modify vscode extension cwd  to better support subtree configuration files
* Fix vscode extension args type configuration
* Support multiple config files

Closes #121
Closes #123
Closes #125
Closes #128
Closes #129
Closes #131
  • Loading branch information
cheshirekow committed Sep 26, 2019
1 parent 7a9c54f commit 7abe24f
Show file tree
Hide file tree
Showing 21 changed files with 270 additions and 72 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ node_modules

# bazel sucks...
WORKSPACE

# Temp/backup files
*~

5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ pkg_find(PKG eigen3
PKG fontconfig
PKG freetype2
PKG fuse
PKG glib-2.0
PKG gnuradio-osmosdr
PKG gnuradio-filter
PKG gtk+-3.0
PKG gtkmm-3.0
PKG libcurl
PKG libglog
PKG libpng NAMES libpng12 libpng16
PKG librtlsdr
PKG libudev
PKG tinyxml2
PKG vulkan
PKG x11-xcb)

Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
10 changes: 7 additions & 3 deletions cmake/codestyle.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function(format_and_lint module)
list(APPEND cmake_files_ ${arg})
elseif(arg MATCHES ".*\.py")
list(APPEND py_files_ ${arg})
elseif(arg MATCHES ".*\.(cc|h)")
elseif(arg MATCHES ".*\.(c|cc|h)")
list(APPEND cc_files_ ${arg})
elseif(arg MATCHES ".*\.js(\.tpl)?")
list(APPEND js_files_ ${arg})
Expand Down Expand Up @@ -84,8 +84,12 @@ function(format_and_lint module)
endif()
if(py_files_)
list(APPEND fmtcmds_ COMMAND autopep8 -i ${py_files_})
list(APPEND lntcmds_ COMMAND env PYTHONPATH=${CMAKE_SOURCE_DIR}
pylint ${py_files_})
# NOTE(josh): --rcfile= is required because some of our python files are
# note entirely within the package tree from the root of the repository.
# As such pylint will not match the rcfile in the root of the repository.
list(APPEND lntcmds_
COMMAND env PYTHONPATH=${CMAKE_SOURCE_DIR}
pylint --rcfile=${CMAKE_SOURCE_DIR}/pylintrc ${py_files_})
# NOTE(josh): flake8 tries to use semaphores which fail in our containers
# https://bugs.python.org/issue3770 (probably due to /proc/shmem or
# something not being mounted)
Expand Down
11 changes: 10 additions & 1 deletion cmake/pkgconfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function(_pkg_query outvar arg)
# Convert space-separated list to semicolon-separated cmake-list
string(REGEX REPLACE " +" ";" _include_list "${_include_dirs}")


set(pkg_${outvar}_includedirs ${_include_list} CACHE STRING
"include directories for ${outvar}" FORCE)

Expand All @@ -30,7 +31,15 @@ function(_pkg_query outvar arg)
return()
endif()

set(pkg_${outvar}_cflags ${_pkg_out} CACHE STRING
# Convert space-separated list to semicolon-separated cmake-list
string(REGEX REPLACE " +" ";" _cflags "${_pkg_out}")
# Convert some C++ specific flags into a generator expression that will
# nullify during C compiles. Specifically match replace strings like
# "-std=c++11" to "$<$<COMPILE_LANGUAGE:CXX>:-std=c++11>".
string(REGEX REPLACE "(-std=[^;]+)" "$<$<COMPILE_LANGUAGE:CXX>:\\1>"
_cflags "${_cflags}")

set(pkg_${outvar}_cflags ${_cflags} CACHE STRING
"cflags directories for ${outvar}" FORCE)

execute_process(COMMAND pkg-config --libs-only-L ${arg}
Expand Down
2 changes: 1 addition & 1 deletion cmake_format/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
"""
from __future__ import unicode_literals

VERSION = '0.5.4'
VERSION = '0.5.5'
106 changes: 80 additions & 26 deletions cmake_format/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

import argparse
import collections
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping
import io
import json
import logging
Expand Down Expand Up @@ -173,6 +177,17 @@ def load_yaml(config_file):
return out


def exec_pyconfig(configfile_path, config_dict=None):
if config_dict is None:
config_dict = {}
config_dict["__file__"] = os.path.realpath(configfile_path)
with io.open(configfile_path, 'r', encoding='utf-8') as infile:
# pylint: disable=exec-used
exec(infile.read(), config_dict)
config_dict.pop("__file__")
return config_dict


def try_get_configdict(configfile_path):
"""
Try to read the configuration as yaml first, then json, then python.
Expand All @@ -193,52 +208,90 @@ def try_get_configdict(configfile_path):
pass

try:
config_dict = {}
with io.open(configfile_path, 'r', encoding='utf-8') as infile:
# pylint: disable=exec-used
exec(infile.read(), config_dict)
return config_dict
return exec_pyconfig(configfile_path)
except: # pylint: disable=bare-except
pass

raise RuntimeError("Failed to parse {} as any of yaml, json, or python"
.format(configfile_path))


def get_config(infile_path, configfile_path):
def get_config_dict(configfile_path):
"""
Return a dictionary of configuration options read from the given file path.
If the filepath has a known extension then we parse it according to that
extension. Otherwise we try to parse is using each parser one by one.
"""
if configfile_path.endswith('.json'):
with io.open(configfile_path, 'r', encoding='utf-8') as config_file:
return json.load(config_file)

if configfile_path.endswith('.yaml'):
with io.open(configfile_path, 'r', encoding='utf-8') as config_file:
return load_yaml(config_file)

if configfile_path.endswith('.py'):
return exec_pyconfig(configfile_path)

return try_get_configdict(configfile_path)


def map_merge(output_map, increment_map):
"""
Merge `increment_map` into `output_map` recursively.
"""
for key, increment_value in increment_map.items():
if key not in output_map:
output_map[key] = increment_value
continue

existing_value = output_map[key]
if isinstance(existing_value, Mapping):
if isinstance(increment_value, Mapping):
map_merge(existing_value, increment_value)
else:
logger.warning(
"Cannot merge config %s of type %s into a dictionary",
key, type(increment_value))
continue

output_map[key] = increment_value

return output_map


def get_config(infile_path, configfile_paths):
"""
If configfile_path is not none, then load the configuration. Otherwise search
for a config file in the ancestry of the filesystem of infile_path and find
a config file to load.
"""
if configfile_path is None:
configfile_path = find_config_file(infile_path)
if configfile_path is not None:
configfile_path = os.path.expanduser(configfile_path)
if configfile_paths is None:
inferred_configpath = find_config_file(infile_path)
if inferred_configpath is None:
return {}
configfile_paths = [inferred_configpath]

config_dict = {}
if configfile_path:
with io.open(configfile_path, 'r', encoding='utf-8') as config_file:
if configfile_path.endswith('.json'):
config_dict = json.load(config_file)
elif configfile_path.endswith('.yaml'):
config_dict = load_yaml(config_file)
elif configfile_path.endswith('.py'):
config_dict = {}
with io.open(configfile_path, 'r', encoding='utf-8') as infile:
# pylint: disable=exec-used
exec(infile.read(), config_dict)
else:
config_dict = try_get_configdict(configfile_path)
for configfile_path in configfile_paths:
configfile_path = os.path.expanduser(configfile_path)
increment_dict = get_config_dict(configfile_path)
map_merge(config_dict, increment_dict)

return config_dict


def yaml_odict_handler(dumper, value):
"""
Represent ordered dictionaries as yaml maps.
"""
return dumper.represent_mapping(u'tag:yaml.org,2002:map', value)


def yaml_register_odict(dumper):
"""
Register an order dictionary handler with the given yaml dumper
"""
dumper.add_representer(collections.OrderedDict, yaml_odict_handler)


Expand Down Expand Up @@ -367,8 +420,9 @@ def setup_argparser(arg_parser):
help='Where to write the formatted file. '
'Default is stdout.')

arg_parser.add_argument('-c', '--config-file',
help='path to configuration file')
arg_parser.add_argument(
'-c', '--config-file', '--config-files', nargs='+',
help='path to configuration file(s)')
arg_parser.add_argument('infilepaths', nargs='*')
add_config_options(arg_parser)

Expand Down Expand Up @@ -455,7 +509,7 @@ def main():
newline='')
else:
if args.outfile_path == '-':
# NOTE(josh): The behavior or sys.stdout is different in python2 and
# NOTE(josh): The behavior of sys.stdout is different in python2 and
# python3. sys.stdout is opened in 'w' mode which means that write()
# takes strings in python2 and python3 and, in particular, in python3
# it does not take byte arrays. io.StreamWriter will write to
Expand Down
4 changes: 2 additions & 2 deletions cmake_format/doc/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ Usage
-i, --in-place
-o OUTFILE_PATH, --outfile-path OUTFILE_PATH
Where to write the formatted file. Default is stdout.
-c CONFIG_FILE, --config-file CONFIG_FILE
path to configuration file
-c CONFIG_FILE [CONFIG_FILE ...], --config-file CONFIG_FILE [CONFIG_FILE ...], --config-files CONFIG_FILE [CONFIG_FILE ...]
path to configuration file(s)
Formatter Configuration:
Override configfile options affecting general formatting
Expand Down
24 changes: 24 additions & 0 deletions cmake_format/doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ Changelog
v0.5 series
-----------

v0.5.5
------

* Python config files now have ``__file__`` set in the global namespace
* Add parse support for ``BYPRODUCTS`` in ``add_custom_command``
* Modify vscode extension cwd to better support subtree configuration files
* Fix vscode extension args type configuration
* Support multiple config files

* Closes `#121`_: Support ``BYPRODUCTS``
* Closes `#123`_: Allow multiple config files
* Closes `#125`_: Swap ordering of cwd location in vscode extension
* Closes `#128`_: Include LICENSE.txt in sdist and wheel
* Closes `#129`_: cmakeFormat.args in settings.json yields Incorrect type
* Closes `#131`_: cmakeFormat.args is an array of items of type string

.. __#121: https://github.com/cheshirekow/cmake_format/issues/121
.. __#123: https://github.com/cheshirekow/cmake_format/issues/123
.. __#125: https://github.com/cheshirekow/cmake_format/issues/125
.. __#128: https://github.com/cheshirekow/cmake_format/issues/128
.. __#129: https://github.com/cheshirekow/cmake_format/issues/129
.. __#131: https://github.com/cheshirekow/cmake_format/issues/131


v0.5.4
------

Expand Down
13 changes: 11 additions & 2 deletions cmake_format/doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ v0.5 series
===========

------
v0.5.3
v0.5.5
------

This is a maintanance release fixing a couple of bugs and adding some missing
This is a maintenance release fixing a few minor bugs and enhancements. One
new feature is that the ``--config`` command line option now accepts a list of
config files, which should allow for including multiple databases of command
specifications

------
v0.5.4
------

This is a maintenance release fixing a couple of bugs and adding some missing
documentation. One notable feature added is that, during in-place formatting,
if the file content is unchanged ``cmake-format`` will no-longer write the
file.
Expand Down
Loading

0 comments on commit 7abe24f

Please sign in to comment.