4

Please see Call functions in other files in C++ and OpenCV for initial problem. The code I am using is given there in detail. This is a subproblem.

I have a BASH script:

echo "compiling $1"
if [[ $1 == *.c ]]
then
    gcc -ggdb `pkg-config --cflags opencv` -o `basename $1 .c` $1 `pkg-config --libs opencv`;
elif [[ $1 == *.cpp ]]
then
    g++ -ggdb `pkg-config --cflags opencv` -o `basename $1 .cpp` $1 `pkg-config --libs opencv`;
else
    echo "Please compile only .c or .cpp files"
fi
echo "Output file => ${1%.*}"

The above BASH script is used to compile OpenCV code using ./script.sh input.cpp.
My doubt is how can I modify it to compile multiple files at the same time, like another file with some functions I am using in my main code like:
./script.sh input.cpp functions.cpp


EDIT
As suggested by John B, I modified my BASH script to

for f in "$@"; do
    echo "compiling $f"
    if [[ $f == *.c ]]
    then
        gcc -ggdb `pkg-config --cflags opencv` -o `basename "$f" .c` "$f" `pkg-config --libs opencv`
    elif [[ $f == *.cpp ]]
    then
        g++ -ggdb `pkg-config --cflags opencv` -o `basename "$f" .cpp` "$f" `pkg-config --libs opencv`
    else
        echo "$f is not .c or .cpp file"
    fi
    echo "Output file => ${f%.*}"
done

But now, I get the following error:

compiling draw_shapes.cpp
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Output file => draw_shapes
compiling test.cpp
/tmp/ccW65wuP.o: In function `main':
/home/kanishka/Desktop/opencv test/test.cpp:31: undefined reference to `draw_line(cv::Mat, cv::Point_<int>, cv::Point_<int>, int, int, int)'
collect2: error: ld returned 1 exit status
Output file => test
Community
  • 1
  • 1
Kanishka Ganguly
  • 1,252
  • 4
  • 18
  • 38
  • Related to: http://stackoverflow.com/questions/24442836/call-functions-in-other-files-in-c-and-opencv – Fantastic Mr Fox Jun 27 '14 at 02:32
  • +1 for linking. Thanks @Ben! – Kanishka Ganguly Jun 27 '14 at 02:34
  • 3
    The normal best practice is to use a `makefile`, or one of the more modern equivalents. `make` was invented to avoid such shell scripts many æons ago. – Jonathan Leffler Jun 27 '14 at 03:47
  • @JonathanLeffler please help with that. – Kanishka Ganguly Jun 27 '14 at 03:49
  • @JonathanLeffler how come this error doesn't occur when I call functions in a C file? This is happening only with C++ files. – Kanishka Ganguly Jun 27 '14 at 03:51
  • Please search for how to build a makefile…there are lots of places on the web that describe how to do it, and I don't wish to spend the time repeating what they say. As to 'how come this error does not occur', I'm not clear which error you are referring to; there is no mention of error in the question. Note that if you have multiple source files that make up a single program, you will need to ensure they are compiled and linked together. Your code rather assumes each program will have a single source file, which is a restrictive definition of how to build a program. Again, `make` handles it. – Jonathan Leffler Jun 27 '14 at 03:51
  • @JonathanLeffler please see [link](http://stackoverflow.com/questions/24442836/call-functions-in-other-files-in-c-and-opencv) as posted by Ben for the related problem. When I link functions in a C file, I only need to compile the main code, and the dependencies are automatically compiled. The same cannot be said for C++ files. Why? – Kanishka Ganguly Jun 27 '14 at 03:54
  • (a) Update your question with the extra information. (b) We can't tell because we can't see your source code. (c) It appears that you are calling a function in your C++ code that is not defined; presumably, your C code doesn't make that mistake. – Jonathan Leffler Jun 27 '14 at 03:56
  • You have to either compile both `.cpp` files at once and choose which name to use as the output program name (noting that `test` is a very bad name for a program on Unix because the shell has a built-in `test` command), or you compile the `.cpp` files to object files (`.o` suffix) and then link the two `.o` files to create the executable. While you only link one or the other, you will get missing references (either to `main()` or to the function `draw_line()`). – Jonathan Leffler Jun 27 '14 at 04:07

4 Answers4

1

As Jonathan Leffler suggests, it's better practice to use a makefile. But if you want to use a Bash script, you could call all the arguments with $@ and iterate over each.

for f in "$@"; do
    echo "compiling $f"
    if [[ $f == *.c ]]
    then
        gcc -ggdb `pkg-config --cflags opencv` -o `basename "$f" .c` "$f" `pkg-config --libs opencv`
    elif [[ $f == *.cpp ]]
    then
        g++ -ggdb `pkg-config --cflags opencv` -o `basename "$f" .cpp` "$f" `pkg-config --libs opencv`
    else
        echo "$f is not .c or .cpp file"
    fi
    echo "Output file => ${f%.*}"
done
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
John B
  • 3,566
  • 1
  • 16
  • 20
  • Never `$@`; always [`"$@"`](http://stackoverflow.com/questions/255898/how-to-iterate-over-arguments-in-bash-script/256225#256225)! – Jonathan Leffler Jun 27 '14 at 03:57
  • @KanishkaGanguly: Please!!! Update the question with the extra information! You can't format in comments. – Jonathan Leffler Jun 27 '14 at 04:08
  • @KanishkaGanguly My answer can only address the information in your question which asks how to execute your script on multiple file arguments. – John B Jun 27 '14 at 04:11
1

To problem stated in edit:

To get files compiled when they don't have the "main" function, You will need to use -c switch, so compiler produces objects instead of executables. The proces should look like this:

g++ $src_file1 $src_file2 -Iincludes_directory -c -o obj/object1.o
g++ $src_file3 $src_file4 -Iincludes_directory -c -o obj/object2.o
g++ $src_file5 $src_file6 -Iincludes_directory -c -o obj/object3.o
....
g++ $srcfileX $src_file_containing_main_function -Iincludes_directory -c -o obj/object_with_main.o

When You have the objects done, it is time for linking them together

g++ obj/* -o my_awesome_executable
morynicz
  • 2,322
  • 2
  • 20
  • 34
0

A straightforward and simplistic solution:

Create another script file with the following content:

./script.sh input.cpp
./script.sh functions.cpp

Then, run it without arguments:

./doit.sh

Your new script, doit.sh, must reside in the same folder as your existing script.sh.

Please consider investing time into learning a decent build system, such as cmake. It could be an overkill for a small project, but it becomes worth the investment when your projects grow in size.

Oleg
  • 1,037
  • 7
  • 13
  • this didn't work. I still get the error `undefined reference to 'draw_line(cv::Mat, cv::Point_, cv::Point_, int, int, int)'` – Kanishka Ganguly Jun 27 '14 at 03:39
  • @KanishkaGanguly, have you verified you are getting the appropriate libs returned by `pkg-config --libs opencv`. Also make sure you are calling the script in the same directory containing your sources. Don't do `./script.sh /some/path/to/input.cpp` – David C. Rankin Jun 27 '14 at 04:18
  • @DavidC.Rankin Yes. The code runs perfectly for one single input file. The error occurs when trying to call functions from a linked C++ file. – Kanishka Ganguly Jun 27 '14 at 04:19
  • I rewrote the script. For homework, do look into makefiles. They just take a short learning curve, then you will have a solid tool to help with compiling. That being said, I still use bash scripts in certain circumstances as well. But you will learn you can pass filenames to makesfiles as well, so they can be called via a script multiple times as well. – David C. Rankin Jun 27 '14 at 04:37
0

Rather than debug, it's easier to just write. Give this a go:

#!/bin/bash

test -n "$1" || { echo "error: insufficient input, usage :${0##*/} filename.c (cpp)"; exit 1; }

for source in "$@"; do

    ext="${source##*\.}"  # file extension
    fname="${source##*/}" 
    fname="${fname%\.*}"  # base filename (no extension)

    if test "$ext" == "c" ; then

        gcc -ggdb `pkg-config --cflags opencv` \
        -o "$fname" "$source" \
        `pkg-config --libs opencv`

    elif test "$ext" == "cpp" ; then

        g++ -ggdb `pkg-config --cflags opencv` \
        -o "$fname" "$source" \
        `pkg-config --libs opencv`

    else
        echo "Please compile only .c or .cpp files"

    fi

done

exit 0
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Please explain this code. Also, how would this be different from what John B suggested? – Kanishka Ganguly Jun 27 '14 at 04:41
  • It is basically the same code, but I don't like using external tools to separate filename.ext with basename for example. I just use bash pattern matching/substring matching/etc... I use the same loop to run your code once for each input file: `for source in "$@"; do`. After that, I just separate the filename/ext from the original `source` filename. Then it is basically your script for `c` or `cpp` files. – David C. Rankin Jun 27 '14 at 04:55