Is it possible to compile a stream of data rather than compiling a .c file using gcc? for example, is it possible that instead of having my code stored in any xyz.c file, I can directly compile the code?
Use gcc options -x and -
$ echo -e '#include <stdio.h>\nmain(){puts("Hello world");return 0;}' | gcc -xc -ogarbage - && ./garbage && rm garbage
Hello world
The single line command above is made up of the following parts:
echo -e '#include <stdio.h>\nmain(){puts("Hello world");return 0;}' # "source"
| # pipe
gcc -xc -ogarbage - # compile
&& # and
./garbage # run
&& # and
rm garbage # delete
This may answer you question, though it is rarely useful.
You can create a file, stream the code into it, then create another process(that is, the compiler) giving it the file as an argument. Then create another process(that is, the linker) and it will create an exe for you. And finally you can launch that exe as a new process. But why?!:)
Related
Using the command line I get the desired output
$ ./program $(< file.txt)
./program 1 2 3 4 5
But with a makefile
all: program file.txt
./program $(< file.txt)
#rm -f program
program: program.c
gcc program.c -o program
I get the output
gcc program.c -o program
./program
Because in a makefile, the $(...) syntax is used for variable interpolation. So your makefile tries to expand the value of a makefile variable / environment variable named < file.txt. And if unset, it expands to an empty string.
Proof:
all:
echo $(< file.txt)
and file.txt containing
now it works
Then execute
% env '< file.txt=Hello world' make
echo Hello world
Hello world
i.e. by setting an environment variable named < file.txt to value Hello world , the greeting was printed. The fix is to escape the $ character by doubling it:
all:
echo $$(< file.txt)
and then
% make
echo $(< file.txt)
now it works
Q.E.D.
Finally, while the $() interpolation syntax in POSIX shells, the $(< file.txt) is not but you can replace it with $(cat file.txt) so it works with minimally POSIX-conforming shells. Of course in a makefile you again need to double the dollar, therefore getting the maximally compatible
$$(cat file.txt)
Alternatively you can use the similar makefile facility which is $(shell ), i.e.
$(shell cat file.txt)
would work too... (now with one $). Finally you can read files with $(file ) GNU makefile function too, i.e.
all:
echo $(file <file.txt)
would work alike but wouldn't call shell at all.
The substitution you are trying to use is a Bash feature, but make out of the box runs the regular Bourne shell sh, where this syntax is not available (even when sh is a symlink to Bash, as is still common on some Linux distributions).
Requiring the contents of the file to be specified on the command line looks like a design flaw, anyway; it's probably much better if your C program simply reads and processes standard input (or perhaps accepts a list of file names, and falls back to stdin if none are specified, like many Unix file processing utilities).
If this is just for a test case to run the program with parameters from a file, check out xargs.
xargs ./program <file.txt
If you insist on using Bash-only syntax, add
SHELL=/bin/bash
(or whatever full path is correct on your system); but understand that this limits the portability of your Makefile.
Still, you'll need to double any literal dollar sign which should be passed through and exposed to the shell.
I'm new to C, and I understand that both of those commands accomplish the same thing, but does one of them do something different than the other along the way?
First of all, if you are using make then for hello.c you will call make as make hello and not make hello.c. Also, note that make is most of the times used with a Makefile. Nonetheless, you can build executable binary from single source file using make as you have shown.
If the executable (hello) does not exist, then both will have same effect - create the executable from source file.
However, if the executable already exists, then make will run the build commands only if it thinks that the source code has changed after last build, whereas cc will always do the build.
For example:
$ make hello
cc hello.c -o hello
$ make hello
make: 'hello' is up to date. # make does not think source file has changed
$ touch hello.c # Update the timestamp of hello.c
$ make hello
cc hello.c -o hello # make thinks source file changed. Builds again
$
However, cc will not check if the source has changed or not. It will always do the required build.
$ cc hello.c -o hello
$ ls -l hello | cut -d ' ' -f '8-'
12:18 hello
$ cc hello.c -o hello # Build again without changing source
$ ls -l hello | cut -d ' ' -f '8-'
12:21 hello # hello was built again
$
Above description was for GNU make and GNU cc. Not sure about other implementations.
P.S.: make is not a compiler. It only calls the compiler when it thinks it should, as seen in the example above. Whereas cc is a compiler.
P.S. If you run cc hello.c, the excutable is named a.out, and not hello.
make(1) is a program that will run commands. You create a file named "Makefile" in a directory. The Makefile has a recipe with a specific syntax that is beyond the scope of this answer. You then invoke the make command in the directory with the file named Makefile
cc $filename will invoke a C compiler upon the $filename
cc or gcc or clang will invoke the compiler.
make is used when you have many files to compile.
In make you will give name of .o file and include header file location.
Can I use Preprocessor Directives in .ld file?
I need to to use one of two sets of .ld file and wants to let Build engine deside that using macro, Can I do that?
Yes, you can. You need to run preprocessor manually for your linker script, like this:
in="your_linker_script.ld"
out="generated_script.ld"
cflags=-Iinclude/
gcc -E -P -x c $cflags $in >$out
Flags:
-E specifies GCC to only run preprocessor
-P prevents preprocessor from generating linemarkers (#line directives)
-x c tells GCC to treat your linker script as C source file (it's needed to run GCC with your LD script)
Or you can simply use cpp tool, which is actually C preprocessor.
After this you will be able to use generated linker script to build your program (e.g. in Makefile).
Example
Here is how I solved this problem in my project:
Here is my linker script uses preprocessor (#include directive and CONFIG_TEXT_BASE constant). Excerpt:
#include <config.h>
. = CONFIG_TEXT_BASE;
Here is script to generate preprocessed linker script. Excerpt:
gcc -E -P -x c -Iinclude $cflags $in >>$out
Here is my Makefile, it's generating preprocessed linker script at $(LDS_GEN) target (line 53) and the this generated script is being used to build result binary (line 42). Excerpt:
$(LDS_GEN): $(LDS)
build/gen-lds.sh $(LDS) $(LDS_GEN) $(CFLAGS)
$(APP).bin: $(OBJS) $(LDS_GEN)
$(LD) $(OBJS) -T $(LDS_GEN) -o $(APP).elf
Small update after long time. This way of pre-processing works until the memory file does not contain lines that are fooling the pre-processor. E.g.:
"KEEP(*path/*.o(.rodata .rodata*))"
The "/*" after path is considered comment start (the line contains what is considered a C multi-line comment start and not a match pattern).
This line is valid for the linker but it is considered comment and the output from C pre-processor will remove everything till a supposed closing comment is found:
"KEEP(*path"
The resulting file is clearly invalid for the linker. I have no solution at the moment.
As a simplification of Sam's answer, I added the below to my makefile and away we go :)
PRE_LD_FILE = $(PROG_NAME).ld
LD_FILE = $(PROG_NAME)_generated.ld
$(LD_FILE) : $(PRE_LD_FILE)
cpp $(PRE_LD_FILE) | grep -v '^#' >>$(LD_FILE)
$ cat $HOME/version.txt
version=1.2.3.4
$ cat hello.c
#include <stdio.h>
int main()
{
printf("Software Version = [%s]\n",VERSION);
return 0;
}
$ cat hello.mk
FILE=$(HOME)/version.txt
VERSION:=`cat $(FILE) | cut -d = -f2 | sed -e 's-^-\\\"-g' -e 's-$-\\\"-g'`
compile :
gcc -o hello hello.c -DVERSION=$(VERSION)
$ cat test.sh
FILE=$HOME/version.txt
# Append with \" at the beginning and end of the string for the compiler
cat $FILE | cut -d = -f2 | sed -e 's-^-\\\"-g' -e 's-$-\\\"-g'
OUTPUT of test.sh :
\"1.2.3.4\"
Problem :
I am trying to write a makefile (hello.mk) which reads a static file ($HOME/version.txt) which stores version of the software being developed in the form version=x.x.x.x. Makefile reads the file and extracts the version value and passes to the compile line through -D flag. In the 'C' code (hello.c), printing the VERSION detail. The idea is to just maintain the version file for various builds and the build will have the appropriate version number in various logs etc.,
I wrote a simple script test.sh to test the cut | sed option and it works fine from the command line. When I have them in the makefile as a MACRO, I get syntax error for the last $ sed option. Also, when I use some other hacks to get the values from the file, the compile line -DVERSION=$(VERSION) seem to expand to the macro VERSION commands rather than the actual VERSION value.
To test, in the makefile, if I replace VERSION to hardcode value like \"1.2.3.4\, then the compile goes through with desired effect.
The current format of the version file cannot be changed (legacy code and is used by many other tools).
Appreciate if you could help me to resolve this issue or any better way to parse the file.
You need to escape the $ in sed via $$; Additionally, i've used $(shell ...)
mnunberg#csure:/tmp$ make -f hello.mk
cc -o hello hello.c -DVERSION=\"1.2.3.4\"
mnunberg#csure:/tmp$ ./hello
Hello. Version is 1.2.3.4
mnunberg#csure:/tmp$ cat hello.mk
FILE=version.txt
VERSION:=$(shell cat $(FILE) | cut -d = -f2 | sed -e 's-^-\\\"-g' -e 's-$$-\\\"-g')
compile :
cc -o hello hello.c -DVERSION=$(VERSION)
You need to quote $ as $$ in make shell scripts. I've also trimmed down your pipeline to just use awk.
hello.mk
...
VERSION := $(shell awk '/^version=/ { split($$0, a, /=/); print a[2];}' $(FILE))
compile :
gcc -o hello hello.c -DVERSION='"$(VERSION)"'
Learnt a lot from both your replies. Was not aware of the '"$(VERSION)"' in the compile line. That eliminated the need to append " while parsing. Also, the shell command was cool. Not sure why it is different from when I did a backquotes (‘`’).
Thanks much again.
Did the following for now.
hello.mk ::
VERSION:=$(shell sed -e 's-^version=--g' $(FILE))
....
compile :
gcc -o hello hello.c -DVERSION='"$(VERSION)"'
.....
I wrote this .sh file to compile any c source file, so that when I run it, it asks for a filename and gcc compiles it and then, runs the executable a.out.
But this doesn't work properly when error is present in .c files. It also shows that a.out is not present. I don't want this error message ( a.out is not present ) but just want to print only the error message generated for the .c files..
Here's the script..
echo `clear`
echo enter file name
read FILE
gcc $FILE
./a.out
echo -e '\n'
If you enable abort-on-error in shell scripts, life will be a lot easier:
#!/bin/sh
set -eu # makes your program exit on error or unbound variable
# ...your code here...
Utilizing builtin rules as an alternative to your script you might want to use make as an alternative to a handcrafted script. To compile file.c and run the generated executable all you need to do is:
make file && ./file
If you don't know it, I strongly suggest you take a look at the make utility as it will ease your work a lot. Managing anything more than a one file project can get really nasty without it.
You can chain compilation and execution commands:
echo `clear`
echo enter file name
read FILE
gcc $FILE && ./a.out
echo -e '\n'
Here, if gcc will fail, the shell will drop the ./a.out command.
You may also protect the file name with double quotes :
#! /bin/bash
clear
echo -n "Enter file name: "
read FILE
gcc -Wall -W "$FILE" && ./a.out
echo
I there hope this is what you were looking it only requires this command:
./compile executableName myCProgram.c -lm
you can place more C files ahead of each others and add more libraries at the end of the line and executableName does not require the .exe
#!/bin/bash
args="$#"
quant=$#
#Copies in case you need the -lm(math library) params
biblio=${args#*-}
#grabs all params except the first one which should be the name of the executable
firstCommand=${*:2:${#args}}
#Remove the "-lm -lc" from firstCommand
firstCommand=${firstCommand%%-*}
printf "\nEXECUTING: gcc -W -Wall -c $firstCommand\n"
#Creates the object file ".o"
gcc -W -Wall -c $firstCommand
#Convert the files names from example.c to example.o
args=${args//.c/.o}
printf "\nEXECUTING: gcc -o $args\n\n"
#Creates the executable
gcc -o $args
printf "\n**Now execute comand: ./$1 **\n\n"