Skip to content

Commit

Permalink
Merge branch 'develop' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
rdnetto committed Jun 24, 2015
2 parents 6aecb04 + 8064623 commit 8c8a43e
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 19 deletions.
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,57 @@ It works by building the project with a fake toolchain, which simply and filters

It is reasonably fast, taking ~10 seconds to generate a configuration file for the Linux kernel.

## Installation
Add ```NeoBundle 'rdnetto/YCM-Generator'``` to your vimrc (or the equivalent for your plugin manager).

Alternatively, Arch Linux users can install YCM-Generator using the (unofficial) [AUR package](https://aur4.archlinux.org/packages/ycm-generator-git/).

## Usage
Run ```./config_gen.py PROJECT_DIRECTORY```, where ```PROJECT_DIRECTORY``` is the root directory of your project's build system (i.e. the one containing the root Makefile, etc.)

YCM-Generator can also be used as a Vim plugin. Once installed with Vundle/NeoBundle/etc., use the ```:YcmGenerateConfig``` or ```:CCGenerateConfig``` command to generate a config file for the current directory. This command accepts the same arguments as ```./config_gen.py```, but does not require the project directory to be specified (it defaults to the current working directory).
You can also invoke it from within Vim using the ```:YcmGenerateConfig``` or ```:CCGenerateConfig``` commands to generate a config file for the current directory. These commands accept the same arguments as ```./config_gen.py```, but do not require the project directory to be specified (it defaults to the current working directory).

## Requirements and Limitations
* Requirements:
+ Python 2
+ Clang

* Supported build systems:
+ Make
+ CMake
+ Autotools
+ make
+ cmake
+ qmake
+ autotools

Your build system should support specifying the compiler through the ```CC```/```CXX``` environment variables, or not use an absolute path to the compiler.

Some flags present in the resulting configuration file may be mutually exclusive with each other - reading the generated file prior to use is recommended.

# Documentation & Support
## Documentation & Support
* run ```./config_gen.py --help``` to see the complete list of supported options.
* if you receive the error message ```ERROR: No commands were logged to the build logs```, try using the ```--verbose``` flag to see any error messages
+ some build systems require certain environment variables to be set. Note that these will *not* be used by YCM-Generator by default, unless `--preserve-environment` is used
+ if you open an issue regarding this error message, please include the output when running with ```--verbose``` and a link to the project repo (if possible)

## Development
Patches are welcome. Please submit pull requests against the ```develop``` branch.

### Windows support
The script is currently supported under Unices (Linux, BSD, OS X) only.
The script is currently supported under Unices (Linux, NixOS<sup>[1]</sup>, BSD, OS X) only.
Implementing Windows support consists of porting the contents of ```fake-toolchain/Unix```.
If you are interested in implementing/testing this, please open a pull request.

<sup>[1]</sup> May require `--preserve-environment` - see [#19](https://github.com/rdnetto/YCM-Generator/issues/19)

### Test Cases
The following projects are used for testing:

| Project | Build system | Notes |
| ------------------------------------------------------------------------- | ----------------- | ------ |
| [Linux kernel](https://git.kernel.org) | Kbuild (Make) | |
| [Vim-qt](https://rdnetto@bitbucket.org/equalsraf/vim-qt.git) | Autotools | |
| [Clementine](https://github.com/clementine-player/Clementine.git) | Cmake | |
| [ExtPlane](https://github.com/vranki/ExtPlane.git) | Qmake | Should be tested with both versions of Qt. |

## License
YCM-Generator is published under the GNU GPLv3.

53 changes: 49 additions & 4 deletions config_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import tempfile
import time
import subprocess
import glob


# Default flags for make
Expand All @@ -30,6 +31,7 @@ def main():
parser.add_argument("-o", "--output", help="Save the config file as OUTPUT. Default: .ycm_extra_conf.py, or .color_coded if --format=cc.")
parser.add_argument("-x", "--language", choices=["c", "c++"], help="Only output flags for the given language. This defaults to whichever language has its compiler invoked the most.")
parser.add_argument("--out-of-tree", action="store_true", help="Build autotools projects out-of-tree. This is a no-op for other project types.")
parser.add_argument("--qt-version", choices=["4", "5"], default="5", help="Use the given Qt version for qmake. (Default: 5)")
parser.add_argument("-e", "--preserve-environment", action="store_true", help="Pass environment variables to build processes.")
parser.add_argument("PROJECT_DIR", help="The root directory of the project.")
args = vars(parser.parse_args())
Expand Down Expand Up @@ -122,7 +124,7 @@ def main():
print("Created {} config file with {} {} flags".format(output_format.upper(), len(flags), lang.upper()))


def fake_build(project_dir, c_build_log_path, cxx_build_log_path, verbose, make_cmd, cc, cxx, out_of_tree, configure_opts, make_flags, preserve_environment):
def fake_build(project_dir, c_build_log_path, cxx_build_log_path, verbose, make_cmd, cc, cxx, out_of_tree, configure_opts, make_flags, preserve_environment, qt_version):
'''Builds the project using the fake toolchain, to collect the compiler flags.
project_dir: the directory containing the source files
Expand All @@ -135,6 +137,7 @@ def fake_build(project_dir, c_build_log_path, cxx_build_log_path, verbose, make_
configure_opts: additional flags for configure stage
make_flags: additional flags for make
preserve_environment: pass environment variables to build processes
qt_version: The Qt version to use when building with qmake.
'''

# TODO: add Windows support
Expand Down Expand Up @@ -167,6 +170,9 @@ def fake_build(project_dir, c_build_log_path, cxx_build_log_path, verbose, make_
# depend upon the existence of various output files
make_args = [make_cmd] + make_flags

# Used for the qmake build system below
pro_files = glob.glob(os.path.join(project_dir, "*.pro"))

# sanity check - make sure the toolchain is available
assert os.path.exists(fake_path), "Could not find toolchain at '{}'".format(fake_path)

Expand All @@ -177,11 +183,22 @@ def run(cmd, *args, **kwargs):

# execute the build system
if(os.path.exists(os.path.join(project_dir, "CMakeLists.txt"))):
# Cmake
# cmake
# run cmake in a temporary directory, then compile the project as usual
build_dir = tempfile.mkdtemp()
proc_opts["cwd"] = build_dir

# if the project was built in-tree, we need to hide the cache file so that cmake
# populates the build dir instead of just re-generating the existing files
cache_path = os.path.join(project_dir, "CMakeCache.txt")

if(os.path.exists(cache_path)):
fd, cache_tmp = tempfile.mkstemp()
os.close(fd)
shutil.move(cache_path, cache_tmp)
else:
cache_tmp = None

print("Running cmake in '{}'...".format(build_dir))
run(["cmake", project_dir] + configure_opts, env=env_config, **proc_opts)

Expand All @@ -192,8 +209,11 @@ def run(cmd, *args, **kwargs):
print("")
shutil.rmtree(build_dir)

if(cache_tmp):
shutil.move(cache_tmp, cache_path)

elif(os.path.exists(os.path.join(project_dir, "configure"))):
# Autotools
# autotools
# perform build in-tree, since not all projects handle out-of-tree builds correctly

if(out_of_tree):
Expand All @@ -216,8 +236,33 @@ def run(cmd, *args, **kwargs):
else:
run([make_cmd, "maintainer-clean"], env=env, **proc_opts)

elif(pro_files):
# qmake
# make sure there is only one .pro file
if len(pro_files) != 1:
print("ERROR: Found {} .pro files (expected one): {}.".format(
len(pro_files), ', '.join(pro_files)))
sys.exit(1)

# run qmake in a temporary directory, then compile the project as usual
build_dir = tempfile.mkdtemp()
proc_opts["cwd"] = build_dir
env_config["QT_SELECT"] = qt_version
env_config["QMAKESPEC"] = "unsupported/linux-clang" if qt_version == "4" else "linux-clang"

print("Running qmake in '{}' with Qt {}...".format(build_dir, qt_version))
run(["qmake"] + configure_opts + [pro_files[0]], env=env_config,
**proc_opts)

print("\nRunning make...")
run(make_args, env=env, **proc_opts)

print("\nCleaning up...")
print("")
shutil.rmtree(build_dir)

elif(any([os.path.exists(os.path.join(project_dir, x)) for x in ["GNUmakefile", "makefile", "Makefile"]])):
# Make
# make
# needs to be handled last, since other build systems can generate Makefiles
print("Preparing build directory...")
run([make_cmd, "clean"], env=env, **proc_opts)
Expand Down
2 changes: 1 addition & 1 deletion fake-toolchain/Unix/ar
2 changes: 1 addition & 1 deletion fake-toolchain/Unix/as
4 changes: 2 additions & 2 deletions fake-toolchain/Unix/cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/bin/bash
#!/bin/sh

if [[ ! -z "$YCM_CONFIG_GEN_CC_PASSTHROUGH" ]]; then
# Cmake determines compiler properties by compiling a test file, so call clang for this case
$YCM_CONFIG_GEN_CC_PASSTHROUGH $@

elif [[ "$@" == "-v" ]]; then
elif [ "$@" = "-v" ]; then
# Needed to enable clang-specific options for certain build systems (e.g. linux)
echo "clang version 3.5.0 (fake toolchain)"

Expand Down
4 changes: 2 additions & 2 deletions fake-toolchain/Unix/cxx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/bin/bash
#!/bin/sh

if [[ ! -z "$YCM_CONFIG_GEN_CXX_PASSTHROUGH" ]]; then
# Cmake determines compiler properties by compiling a test file, so call clang for this case
$YCM_CONFIG_GEN_CXX_PASSTHROUGH $@

elif [[ "$@" == "-v" ]]; then
elif [ "$@" = "-v" ]; then
# Needed to enable clang-specific options for certain build systems (e.g. linux)
echo "clang version 3.5.0 (fake toolchain)"

Expand Down
1 change: 1 addition & 0 deletions fake-toolchain/Unix/g++
2 changes: 1 addition & 1 deletion fake-toolchain/Unix/ld
2 changes: 1 addition & 1 deletion fake-toolchain/Unix/nm
4 changes: 4 additions & 0 deletions fake-toolchain/Unix/true
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
# This script is needed because /bin/true does not exist on non-FHS-compliant distros. e.g. NixOS
exit 0

2 changes: 1 addition & 1 deletion plugin/ycm-generator.vim
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function! s:GenerateConfig(fmt, flags)
" Only append the working directory if the last option is a flag
let l:split_flags = split(a:flags)
if len(l:split_flags) == 0 || l:split_flags[-1] =~ "^-"
let l:cmd = l:cmd . " " . getcwd()
let l:cmd = l:cmd . " " . fnameescape(getcwd())
endif

execute l:cmd
Expand Down

0 comments on commit 8c8a43e

Please sign in to comment.