one-file (1f) compile
from: one-file (1f) Informer v1.0.1
<< snip >>
Source
======
There is a source file embeded in this program, so all the following applies to this program as well.
Any one-file (1f) program fully compiled will have a source file appended to it. The source contains code to output that source file to the console, which can be save to a file. Only TEXT files (usually .cpp) and ZIP (.zip) sources are attached, with the '--src-type' switch giving the required file extention.
TEXT & ZIP are available on 99.9% of platform architectures. ZIP is only used if the one-file (1f) program requires more than one source file, or has extra data files. The ZIP may also contain a 'Makefile', possibly even a './configure' script, but these are NOT a requirement.
The primary one-file (1f) source file contains a comment block at the top '/* one-file (1f) compile' that has all the commands used to compile the source and attach it to the binary. It is multi-line shell code, so any line (without the last \) can be pasted into the command prompt to execute that part of the build process.
'source-1f' gives you info about a one-file (1f) source file, including extracting this block to the console. 'make-1f' uses this block to compile, check file sizes, re-compile with sizes, then append source. 'make-1f' is just meant to be a mid-way point, before the need to use a full blown 'Makefile'.
one-file (1f) sources do not have to do things the traditional way. Its intended to keep things simple (build wise) and easy to update or change (eg different compiler).
If you have a large set of fuctions outside your 'main' block, once they have been fully debugged you can place them in another file and use the 'include' to hack them back in place in your main source file. This removes the need for a 'Makefile' or compiling multiple objects.
one-file (1f) source should still be clean and well presented, with 'include's at the top, 'struct's and 'typedef's after that, then 'extern'als if you have a truely multi-file source build. Place 'const'ants in logical places after the 'include's, before the end of 'extern'als.
Make
====
'make-1f' can output a really clean, compact, and simple 'Makefile', when you get to the point that it makes things easier. Otherwise try your best NOT to use 'make', even if you end up creating and using your own 'make.sh' script, make sure you can easily include it into a comment block (eg. /* one-file (1f) 'make.sh').
If you find a 'Makefile' easier, you can include that in into a comment block too (eg. /* one-file (1f) 'Makefile'). Rememeber that the idea of using a one-file (1f) system is to have only one source file that can be attached to your binary after it is compiled.
But this simplicity is only useful if you can get your head around your code, so don't be afraid of making that next step and splitting things up. Try doing it without a 'Makefile' first, thus avoiding seperate object files and linking them.
It is then only a small step to getting the files to function with the use of a 'Makefile', usually just by adding a .h/.hpp header file for each new source file, containing the required 'extern's etc, and adding 'include's in your main source.
The 'Makefile' generated by 'make-1f' is a straight copy of the one used by Parallel Realities in their first C++ cross-platform (Linux/Unix) game, Project: Starfighter. After 10 Years it is still a pleasure to use, easy to adjust, and compatible with current systems.
<< snip >>
end: one-file (1f) Informer v1.0.1
Skeleton
========
There is a one-file (1f) skeleton .CPP in TEXT format that can be viewed with additional comments, using the following command:
./one-file-1f skeleton
And output to file with:
./one-file-1f --src > _skel-1f.cpp
Once compiled, the source can then be extracted again, saved directly to a file using:
./_skel-1f --src > _skel-1f.cpp
As of one-file (1f) Informer v1.0.1, the size of '_skel-1f.cpp' (5914 bytes) should match the output of either of the following commands:
./_skel-1f --src-size
./one-file-1f --src-size
This will not be the case (unless I can't remember modifying it) for:
NAME=_name_here_ ./one-file-1f --source > $NAME-1f.cpp
As the size of '$NAME-1f.cpp' will vary from the default '_skel-1f.cpp' by (5 bytes * 21), because '$NAME' replaces '_skel' 21 times before the file is dumped.
Compile
=======
/* one-file (-1f) compile
g++ -Wall -O3 -DVERSION=\"1.0.1\" -o _skel-1f _skel-1f.cpp ; \
g++ -Wall -O3 -DVERSION=\"1.0.1\" -DPRG_SIZE=`ls -l _skel-1f|cut -d \ -f 5` -DSRC_SIZE=`ls -l _skel-1f.cpp|cut -d \ -f 5` -o _skel-1f _skel-1f.cpp ; \
cat _skel-1f.cpp >> _skel-1f
*/
The compile instruction, which is written in such a way that it can be copied directly to a shell console and executed, is a 2 pass compile. The second compile works out the size of the compiled binary, the size of the source file, before compiling a 2nd time, and lastly appends the source to the binary.
"/* one-file (-1f) compile" is an identifier, to allow automated script access to the source file. The above mentioned (currently non-existant) make-1f is one such script that allows for a more automated compile regime, from an IDE/RAD frontend, but without going to the full step (and complexity) of creating a Makefile and using GNU/BSD make.
However, should you feel the need, there is a "cheap" Makefile method, simply it lacks the complexity of a full Makefile of the modern variety. It was found in the source archive of Project: Starfighter, dated to around 2000, it was over 16 years old and worked out of the box on a Raspberry Pi running Raspbian Wheezy, which is why is was chosen.
In that case, you still want to maintain the compile instruction in your primary source file. They are there as teaching aids, learning tools, as much as for combating the need to rememeber. The identifier contents also help maintain usability of any one-file (1f) tools which interact with the source (like make-1f), thereby helping to future proof one-file (1f) source code.
There is no hard and fast rule that you have to use GCC or G++. The compile instructions are just text, designed to be executed in a shell or on a command line. Therefore you could just as easily use VCC or even a cross-assembler, as long as your shell can allow you to get a file size in bytes, it will remain useful, even on ancient 8-bit architectures like MSX-DOS.
The main reason you may wish to move to any form of Makefile will probably be too split up your sources into multiple files. Even then you can still avoid the weight of GNU/BSD make by using "#include" pre-processor commands to include your (error free) source files, but it can be a pain to debug if those includes have errors in them.
For this reason it was decided to design one-file (1f) to allow appending ZIP format files, not just TEXT format files. Unlike most other archive formats, ZIP is available on EVERY platform. Just make the remark changes "//" to the appropriate lines in your primary source file (#define SRC_TYPE), so your program outputs the correct extention type, which can then also be used to automate the extraction process. Getting the ZIP format file out of your program is done the same way as with a TEXT format file:
multifile-1f --src > multifile-1f-src.zip
The appending of a ZIP file is the same too, but of course there will be a ZIP step in the compile instructions, before the 2nd pass of the compile. Combined with the above mentioned "cheap" Makefile, you are basically gaurenteed to be able to compile your multi-file source program with GNU/BSD make in another 10-20 years.
There is nothing stoping you from using Cmake or LVM, except that they are complex, and require "fat" build systems, which was part of the reason for one-file (1f) in the first place, to reduce development resources while extending source life-span, especially on embeded devices, where resources like disk space and memory are usually in short supply.
Again there is no hard and fast rule about using ZIP format, as long as the platform you are developing for has a common place archive format, with freely available tools, that newbies can find and use, then use any archive format required, just remember it will be harder to get the source archive back onto a more common platform. Example, using SQeeZe or SQuash format on CP/M, better to use zip which is now available on CP/M.
Examples
========
/* one-file (-1f) compile
g++ -Wall -O3 -DVERSION=\"1.0.1\" -o multifile-1f ./multifile-1f-src/main.cpp ; \
zip -1 -r multifile-1f-src multifile-1f-src.zip
g++ -Wall -O3 -DVERSION=\"1.0.1\" -DPRG_SIZE=`ls -l multifile-1f|cut -d \ -f 5` -DSRC_SIZE=`ls -l multifile-1f-src.zip|cut -d \ -f 5` -o multifile-1f ./multifile-1f-src/main.cpp ; \
cat multifile-1f-src.zip >> multifile-1f
*/
/* one-file (-1f) compile
make -f ./multifile-1f-src/Makefile CFLAGS=-DVERSION=\"1.0.1\" ; \
zip -1 -r multifile-1f-src multifile-1f-src.zip
make -f ./multifile-1f-src/Makefile CFLAGS=-DVERSION=\"1.0.1\"\ -DPRG_SIZE=`ls -l multifile-1f|cut -d \ -f 5`\ -DSRC_SIZE=`ls -l multifile-1f-src.zip|cut -d \ -f 5` ; \
cat multifile-1f-src.zip >> multifile-1f
*/
remember to use $(CFLAGS) in your Makefile, not $CFLAGS, to allow over-ride.
The future is so bright, I gotta wear 'shades-1f' ...
|
©2016 - Paul Wratt - one-file (1f)
|