In Buck, how do I expand a genrule output directory? - c++

I have a genrule that outputs a directory containing C++ headers. I also have a cxx_library which uses the output of the genrule. The headers field of the cxx_library looks like this:
...
headers = [
':my-headers',
],
...
The problem is that my C++ source files include the headers like this:
#include "my_header.h"
But Buck prepares the staging area like this:
my-cxx-library#default,private-headers/out
Where out is an alias to the folder containing my generated headers.
So in order to use the headers, I would have to include them like this:
#include "out/my_header.h"
The library is not my own, so I would not like to change the source-code. Instead, I would like to use something like a subdir_glob to include everything inside :my-headers/out.
I tried this:
...
headers = subdir_glob([
(':my-headers/out', '**/*.h'),
]),
...
However, it seems that when done this way, the string :my-headers does not get resolved to the output path of :my-headers.
Is there a function in buck that can expand a rule to its output path?

This isn't trivially possible today, but there is a workaround you can use:
genrule(
name = 'headers',
cmd = 'generate_headers.py --outdir=$OUT'
out = 'headers-dir',
)
genrule(
name = 'generated_header_A.h',
cmd = 'cp $(location :headers)/genereated_header_A.h $OUT',
out = 'generated_header_A.h',
)
Then, in your cxx_library, you just need to declare your headers by referencing the location:
headers = {
'some_namespace/header_A.h': ':generated_header_A.h',
},
The above code assumes all of this is in the same build file, but if it isn't, you just need to use a fully-qualified build target instead.

Related

CLion new C++ Class file template

CLion has file template to generate C++ Class, which generates source file and header. In my project I have handler classes that have same code part, and i want to generate them by file templates. And templates that i created can't do this:
Set file names for class MyHandlerClass i want my_handler_class.cpp and .hpp
From one class name i want to generate 2 files header and source, don't know how to do that.
I also want to have string like MyClass -> my-class, found function $lowercaseAndDash($NAME) but don't know why its not works
I have template for header:
#pragma once
// includes
namespace handlers {
class ${NAME}: public Parent {
public:
// methods
};
}
I tried to solve first problem by setting the file name like this: #set ($FILE_NAME = "test_class.hpp"). But i don't know hot to set CamelCase to snake, and this don't works.
Also find in docs function to snake case, but its not works for me in file template.
Its impossible to change file name from template. When you create a C++ Class, you set class name and select file name encoding. When you create file from template its saves file with your file name. I found solution this way, enter file_name (in snake case), and with Velocity create CamelCaseName for class name:
#set( $CamelCaseName = "" )
#set( $part = "" )
#foreach($part in $NAME.split("_"))
#set( $CamelCaseName = "${CamelCaseName}$part.substring(0,1).toUpperCase()$part.substring(1).toLowerCase()" )
#end
Its impossible to generate 2 files from one click like it do C++ Class template. I have to templates, and use them both.
This function is for liveTemplates, for FileTemplate i used velocity: #set( $NeededString = $NAME.replaceAll('_', '-') )
To include header file from cpp use #[[#include]]# "${NAME}.hpp"

how to find if prefix is a match to some string in a set in Ruby?

I am trying to go through a predefined dir path set but I only have the prefix of the path
I have tried use include? or find a method that does that.
I can only think about the trivial solution of define a regexp and go for each in the set, but that seems to be not so ruby-like style
require 'set'
legal_paths = Set['A/B/C', 'A/D/E', 'A/F/G']
Dir.glob('**/system.log').each do |fd|
if failed_tests_path.include?(fd) #fd for example = A/F/G/E/system.log, A/B/C/K/system.log etc...
puts fd
end
end
I want fd to be only system.log files that the paths are including inside the set (the set holds the prefix to the path)
If these are only the prefix, try String#start_with?:
require 'set'
legal_paths = Set['A/B/C', 'A/D/E', 'A/F/G']
files = Dir.glob('**/system.log').select do |fd|
fd.start_with?(*legal_paths)
end
Why not make use of the fact that you can specify these prefixes in the glob?
legal_paths = ['A/B/C', 'A/D/E', 'A/F/G']
files = Dir.glob("{#{legal_paths.join(',')}}/**/system.log")
Note though that if the legal_paths are input by the user, the above might not be secure as the user could traverse to parent directories using ...

How to change C++ include guards in CLion?

When CLion creates a header file it adds include guard strings like this:
#ifndef PROJECTNAME_FILENAME_H
#define PROJECTNAME_FILENAME_H
/* ... code ... */
#endif //PROJECTNAME_FILENAME_H
But I want just FILENAME_H without the PROJECTNAME_ prefix. How to change it in CLion settings?
Bit late to this question, but I've got a slightly more involved solution that'll handle this without the need for manual post-processing regardless of file extension:
Head into your File and Code Templates - The other answers already detail how to do this.
In the File and Code Templates settings page, change to the Includes tab.
Click the + to create a new Include template. Name it something like IncludeGuard and set the Extension to h.
Input the following for the contents. Make sure you don't include any blank lines before or after.
#macro( includeGuard $filename $ext )
#set( $ucfull = ${filename.toUpperCase().replace('-', '_')} )
#set( $extidx = ${ucfull.lastIndexOf(".")} )
#set( $extstart = $extidx + 1 )
#if( $extidx > -1 )
#set( $ucname = ${ucfull.substring(0,$extidx)} )
#set( $ucext = ${ucfull.substring($extstart)} )
#else
#set( $ucname = $!{ucfull} )
#set( $ucext = ${ext.toUpperCase()} )
#end
${ucname}_${ucext}##
#end##
Change back to the Files tab, and find the C Header File or C++ Class Header file depending on which language you're looking to update.
Change the contents of this file template to:
#parse("IncludeGuard.h")##
#set( $blank = "" )
#[[#ifndef]]# #includeGuard(${NAME} "h")${blank}
#[[#define]]# #includeGuard(${NAME} "h")${blank}
// ...
#[[#endif]]# // #includeGuard(${NAME} "h")
If everything works as intended, attempting to create a C Header File using the name test-include-guard or test-include-guard.h should both result in the following:
#ifndef TEST_INCLUDE_GUARD_H
#define TEST_INCLUDE_GUARD_H
// ...
#endif /* TEST_INCLUDE_GUARD_H */
Few notes:
If you need to change the file extension, change the includeGuard(${NAME} "h") parts to use whatever extension you want for the second parameter. The template will attempt to parse the file extension from ${NAME}, but ${NAME} only contains the file extension if you explicitly enter it into the new filename dialog.
The current state of whitespace handling in the Velocity templates used by CLion is a shit show, so you'll need to work around this as I did if you decide to further customize the templates. General guidelines:
If you're experiencing undesired linebreaks, you'll want to try adding a terminating line comment ## to the ends of lines before it.
If you find yourself in the oppostie scenario, (missing an expected linebreak) you can work around this with the #set( $blank = "" ) strategy I utilized above.
Most of the IntelliJ-based IDEs seem to cache the compilation of Include templates the first time they get passed into #parse(). If you make changes to an Include template after this point, you'll usually need to use the File > Invalidate Caches/Restart menu command before the changes propagate.
Settings->Editor->File and Code Templates->Files
change ${INCLUDE_GUARD} into _${NAME}_H_
For example, if your file name is: clion.h, then _${NAME}_H_ is rendered as _clion_H_, because ${NAME} is rendered as the filename (without extension).
File | Settings | Editor | File and Code Templates for Windows and Linux
CLion | Preferences | Editor | File and Code Templates for OS X
#[[#ifndef]]# BASE_${HEADER_FILENAME}
#[[#define]]# BASE_${HEADER_FILENAME}
#[[#endif]]# //BASE_${HEADER_FILENAME}
>
#ifndef BASE_test_h
#define BASE_test_h
#endif //BASE_test_h
select BASE_test_h and press CTRL + SHIFT + U to upper case
According to the latest doc (2019.3, but it may work in earlier versions, too) you can navigate to the Naming Convention tab under Settings / Preferences | Editor | Code Style | C/C++.
There you'll find a field that allows you to easily change the header guard pattern. No need to add custom templates anymore.

scons - How to add search directories to an existing scanner

My main goal is to add support of -isystem include paths in scons, like this is proposed here : https://stackoverflow.com/a/2547261/4042960
The solution of creating new variables works fine: I do that:
#### Add support for system headers
env['SYSTEMINCPREFIX'] = '-isystem '
env['SYSTEMINCSUFFIX'] = ''
env['_CPPSYSTEMINCFLAGS'] = '$( ${_concat(SYSTEMINCPREFIX, CPPSYSTEMPATH, SYSTEMINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_CCCOMCOM'] += ' $_CPPSYSTEMINCFLAGS'
I use it by adding for instance:
env.Append(CPPSYSTEMPATH = ['/my/include/path'])
My problem is that now, the path /my/include/path is not scanned by the C (or C++) dependency scanner. After many search, I failed to find how to add my variable "CPPSYSTEMPATH" to be treated like "CPPPATH" by the dependency scanner.
Does anyone know how I could add the search path contained in "CPPSYSTEMPATH" to the existing C scanner ?
I hope that my problem is clear enough, else do not hesitate to tell me.
Here's a basic recipe for replacing the FindPath method of the default C scanner, but be warned it's an ugly hack:
# Create environment
env = Environment()
# Define your new env variable as combination of both paths
env['MYCPPPATHS'] = ['$CPPPATH','$CPPSYSTEMPATH']
# Replace the path_function of the standard C scanner by:
import SCons.Tool
import SCons.Scanner
setattr(SCons.Tool.CScanner,'path_function',SCons.Scanner.FindPathDirs('MYCPPPATHS'))
# Do your build stuff...
env['CPPSYSTEMPATH'] = 'myinclude'
env.Program('main','main.cpp')
By the way, why not ask these kind of questions on our user mailing list scons-users#scons.org? ;)

How can I control infile and outfile passed to compiler when specifying files to compile with django-pipeline?

I've written a rudimentary requirejs compiler for Django-Pipeline and I'm trying to polish it up but I'm stuck with a certain problem. I've noticed the same "problem" with the SASS compiler so I wonder if this is a setting I'm missing or something.
PIPELINE_CSS = {
'main': {
'source_filenames': (
'sass/main.scss',
),
'output_filename': 'css/crushcode.css',
'extra_context': {
'media': 'screen,projection',
}
}
}
PIPELINE_JS = {
'requirejs': {
'source_filenames': (
'js/lib/requirejs-2.0.4.js',
),
'output_filename': 'js/require.js',
'extra_context': {
'data': '/static/js/bootstrap'
}
}
}
Both of these create an output file in the source directory of the same name as the input file, with the extension changed to output_extension of the respective compiler class. For SASS, this is ok as the extension is .css so you end up with main.css next to your main.scss, but with my requirejs plugin the first time I ran it, because the extensions are the same, I actually overwrote my original file (Nothing lost of course, thankyou version control).
I noticed that infile and outfile were both pointing to:
APP_ROOT/static/js/lib/require-2.0.4.js
when I would have thought outfile should point to output_filename in the settings.
The easy fix was to change the output_extension of my custom compiler class to be 'optimized.js', but at this point I'm adding a .gitignore for every compiled file, and not to mention collectstatic then brings everything across, and also creates the desired output_filename file in the target directory.
What I was hoping for was that collectstatic would simply create the js/require.js file in my STATIC_ROOT directory.
It feels like I'm doing something wrong here, any tips? Is this expected behaviour? If so, what's the best should I go about changing it?