Skip to content

Instantly share code, notes, and snippets.

@ryukoposting
Created November 14, 2022 21:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryukoposting/24cba4eda0f691dad9daa2b392ef1ba4 to your computer and use it in GitHub Desktop.
Save ryukoposting/24cba4eda0f691dad9daa2b392ef1ba4 to your computer and use it in GitHub Desktop.
Automatically generate c_cpp_properties.json from Makefile with a 99-line Ruby script
CC:=gcc
CFLAGS:=\
-I./include\
-DFOO\
-DBAR\
-std=c11
.vscode/c_cpp_properties.json: Makefile
ruby vscode_cprops_gen.rb .vscode $(CC) $(CFLAGS)
# Automatically generate VSCode intellisense settings from your makefile.
#
# Usage: ruby vscode_cprops_gen.rb (output_dir) (compiler) (cflags...)
#
# Makefile sample:
# .vscode/c_cpp_properties.json: Makefile
# @ruby vscode_cprops_gen.rb .vscode $(CC) $(CFLAGS)
require 'pathname'
require 'json'
CONFIG_NAME = "Project" # name of the output c/c++ configuration
GENERATE_FLYLINT = true # whether to copy the config into flylint's config
def which(cmd)
# Thanks stackoverflow: https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each do |ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
end
end
nil
end
outdir = Pathname.new(ARGV[0]).cleanpath
compiler_path = Pathname.new(which(ARGV[1])).cleanpath
wd = Pathname.new(Dir.getwd)
workspace_dir = wd.join(outdir).join("..").cleanpath
c_cpp_properties_json = outdir.join "c_cpp_properties.json"
settings_json = outdir.join "settings.json"
include_paths = []
defines = []
c_std = nil
cpp_std = nil
ARGV[2..].each do |arg|
if arg =~ /^-I/
ipath = Pathname.new(arg[2..]).cleanpath
relpath = wd.join(ipath).relative_path_from(workspace_dir)
include_paths.push "${workspaceFolder}/#{relpath}"
elsif arg =~ /^-D/
defines.push arg[2..]
elsif arg =~ /^-std=/
if arg.include? "++"
cpp_std = arg[5..]
else
c_std = arg[5..]
end
end
end
unless File.exists? c_cpp_properties_json
default_json = {
version: 4,
configurations: []
}
File.write c_cpp_properties_json, JSON.pretty_generate(default_json)
end
properties = {
'name' => CONFIG_NAME,
'includePath' => include_paths,
'defines' => defines,
'compilerPath' => compiler_path,
}
properties['cStandard'] = c_std if c_std
properties['cppStandard'] = cpp_std if cpp_std
json = JSON.parse File.read(c_cpp_properties_json)
did_update = json['configurations'].any? do |cfg|
if cfg['name'] == CONFIG_NAME
cfg.merge! properties
end
end
unless did_update
json['configurations'].push properties
end
File.write c_cpp_properties_json, JSON.pretty_generate(json)
if GENERATE_FLYLINT
unless File.exists? settings_json
File.write settings_json, JSON.pretty_generate({})
end
json = JSON.parse File.read(settings_json)
json["c-cpp-flylint.includePaths"] = include_paths
json["c-cpp-flylint.defines"] = defines
json["c-cpp-flylint.standard"] = [c_std, cpp_std].filter { |s| !s.nil? }
File.write settings_json, JSON.pretty_generate(json)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment