8
\$\begingroup\$

I'm currently developing a new version of ExpressGenGen Using TDD. While I'm rewriting the new version of my Code Generator Generator. I'm using its old version to generate code to be used by the new version. This is how I'm creating new classes: when a new class is created, it will generate a .cpp and .h file and then add them to CMakeLists.txt, and also TestHelper.h is updated with new headers.

What I want reviewed are the bash scripts. (You are also welcome to review the ExpressGenGen templates)

Bash Scripts

gen

#!/bin/bash
cd ..
cat Samples/template_bash_CMakeLists.txt | xgengen bash | bash - > CMakeLists.txt
cat Samples/template_bash_TestHelper.txt | xgengen bash | bash - > Tests/TestHelper.h

new_class

#!/bin/bash
echo 'Create New Class'
echo '--------------------------'
echo 'Type Class Name : '
read _class
# convert Upper Camel case to header guard, ex: NewClass -> _NEW_CLASS_H_
_guard=`echo $_class | sed -e 's/\([A-Z]\)/_1円/g' | awk '{printf toupper(0ドル)} END{printf "_H_";}'`
# export the variables so they are visible to the child shell
export guard=$_guard
export class=$_class
cat template_bash_CPP.txt | xgengen bash | bash - > "../Src/$class.cpp"
cat template_bash_H.txt | xgengen bash | bash - > "../Src/$class.h"
./gen

Templates

template_bash_CMakeLists.txt

project(ExpressGenGen)
cmake_minimum_required(VERSION 2.6)
include_directories($ENV{BOOST_ROOT} $ENV{LUA_ROOT} $ENV{GMOCK_HOME}/include $ENV{GMOCK_HOME}/gtest/include .)
link_directories($ENV{LUA_ROOT} $ENV{BOOST_ROOT}/stage/lib $ENV{GMOCK_HOME}/build $ENV{GMOCK_HOME}/build/gtest) 
set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wpedantic -std=gnu++11")
set(sources 
$$$find . -name '*.cpp' | sed 's/\.\// /'
 $$$find . -name '*.h' | sed 's/\.\// /'
 WindowsRsrc/WindowsRes.rc)
add_executable(xgengen ${sources})
target_link_libraries(xgengen lua)
target_link_libraries(xgengen boost_program_options)
target_link_libraries(xgengen pthread)
target_link_libraries(xgengen gmock)
target_link_libraries(xgengen gtest)

template_bash_CPP.txt

//--------------------------------------------
// Definition
#include "{$$$class$$}.h"
//--------------------------------------------
// Uses
//
//--------------------------------------------
{$$$class$$}::{$$$class$$}()
{
}
//--------------------------------------------

template_bash_H.txt

#ifndef {$$$guard$$}
#define {$$$guard$$}
//--------------------------------------------
// Has
//
//--------------------------------------------
// Inherits
//
//--------------------------------------------
// Uses
//
//--------------------------------------------
class {$$$class$$} {
public:
 {$$$class$$}();
 virtual ~{$$$class$$}() {}
private:
};
//-------------------------------------------
#endif // {$$$guard$$}

template_bash_TestHelper.txt

//--------------------------------------------
// TestHelper
//
// This header is basically an include all type helper for Test*.cpp files
// therefore greatly reducing duplication
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <gmock/gmock.h>
#pragma GCC diagnostic pop
#include <iostream>
#include <istream>
#include <sstream>
#include <memory>
// AutoGenerated
$$$find Src -name "*.h" | sed -e 's/\(.*\)/#include \"\.\.\/1円\"/g'
// End AutoGenerated
//--------------------------------------------
200_success
146k22 gold badges190 silver badges478 bronze badges
asked Feb 15, 2015 at 14:01
\$\endgroup\$
2
  • \$\begingroup\$ Note: I cannot fix the indentation \$\endgroup\$ Commented Feb 15, 2015 at 14:55
  • \$\begingroup\$ @janos The line after $$$find . -name '*.cpp' | sed 's/\.\// /'. See meta.codereview.stackexchange.com/questions/5063/… \$\endgroup\$ Commented Feb 15, 2015 at 15:28

1 Answer 1

15
\$\begingroup\$

Use input redirection instead of cat | ...

Instead of cat somefile | somecommand, you should use input redirection, for example:

xgengen bash < Samples/template_bash_CMakeLists.txt | bash - > CMakeLists.txt

The advantage of this is that you're running one fewer process (no more cat).

Use modern style command substitution $(...)

Don't use old style `...` command substitution. Use the modern and better $(...) style.

Simplify sed + awk pipelines when possible

This looks very complicated, and it's suspicious to have both sed and awk in the same pipeline:

echo $_class | sed -e 's/\([A-Z]\)/_1円/g' | awk '{printf toupper(0ドル)} END{printf "_H_";}'

You can simplify using a single awk command:

echo $_class | awk '{gsub("[A-Z]", "_\&"); printf toupper(0ドル) "_H_"}'

Use here-strings instead of echo | ...

Similar to using input redirection instead of cat | ..., in Bash it's better to replace echo | ... with here-strings using <<<, rewriting the above example as:

awk '{gsub("[A-Z]", "_\&"); printf toupper(0ドル) "_H_"}' <<< $_class

Use alternative regex pattern separator to improve readability

When a regular expression pattern contains /, using such patterns with the s/// operator becomes tedious and hard to read, because of the escapes. To improve readability, it's often good to use an alternative pattern separator. So for example instead of this:

sed 's/\.\// /'

You can do this instead to avoid escaping /:

sed 's|\./| |'

The benefit is more obvious when you have multiple / in the pattern. In this example the difference might be negligible.

Simplify regex

The s/// here can be simplified:

sed -e 's/\(.*\)/#include \"\.\.\/1円\"/g'

To this:

sed -e 's/.*/#include "..\/&"/'

That is:

  • No need to capture .* in \(...\), you can access the matched string directly with &
  • No need to escape . in replacement, as it has no special meaning there
  • No need to escape " within single-quoted strings in Bash
  • Since the pattern matches the entire line, no need for the g flag
answered Feb 15, 2015 at 15:04
\$\endgroup\$
3
  • 2
    \$\begingroup\$ This is a very good answer with many interesting suggestions. \$\endgroup\$ Commented Feb 15, 2015 at 18:41
  • 1
    \$\begingroup\$ $() may be better, but to the best of my knowledge it still isn't supported by the default shell on certain System V derived systems. As I see no other reason the scripts above need to be tied to bash rather than any Bourne shell, I'd suggest sticking with backticks and using #!/bin/sh rather than relying on bash being installed, which it may not be in some systems. \$\endgroup\$ Commented Feb 16, 2015 at 8:06
  • \$\begingroup\$ @Jules that won't work considering that xgengen generate bash code. But what you said gave me an idea that I should make it generate sh code as well. \$\endgroup\$ Commented Feb 20, 2015 at 11:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.