123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- ===========================
- Chapter 15: linking to SDL2
- ===========================
- What you need to know before you read this:
- * Linux: bash shell
- * Linux: text editors
- * Linux: exit code
- * Linux: man pages
- * Git: repositories
- * Git: clone
- * Git: commits
- * Programming: libraries
- * C programming: functions
- * C programming: function arguments
- * C programming: command line arguments
- * C preprocessor: #include
- * C library: printf()
- * GCC: compiling stage
- * GCC: linking stage
- What this chapter will teach you:
- * GCC: link to external library
- * SDL2: SDL_Delay
- * ldd: show links
- Projects worked on:
- * Cuterald
- Repo: http://hqetojnktk6trragksiuszsodbuszhmekulsbtp2bfumvh3i7sfiyoyd.onion:80/Maple/cuterald.git
- Current commit: 9c3a2e731ec03644bd7f0c92cca3e91d4d04aec4
- Section I: fetch or pull the reference repository
- -------------------------------------------------
- What follows is a quick reminder on how to pull my version of the code that we expect to start with
- this chapter. There's benefit in knowing how to do this - your code might diverge from mine and
- cause some bugs that don't happen on the "canonical" version. I encourage working on your own code
- and experimenting as you wish, but if you ever run into problems or errors that you can't figure
- out, it helps to have a reference that is known to work so you can compare things.
- What needs to be done depends on how when (or if) you've had interactions with the repository
- before.
- * Scenario 1: you never pulled the repo
- If you never pulled my code at all, follow these steps:
- 1. Navigate from bash (or another shell) to the folder where you want to put the project folder.
- 2. Run `torsocks clone <repo>`, where '<repo>' is the URL you see in the header for this chapter,
- without the greater than / less than signs.
- 3. A new directory is present (in this case, "cuterald"). Enter it.
- 4. Run git checkout on the hash I've provided.
- * Scenario 2: you already have a copy
- 1. Navigate, in your shell, to the folder where the example code lives.
- 2. Run `torsocks git fetch` to make sure all commits from the remote are available to you.
- Fetch talks to the remote and gets all data from it, but doesn't do anything with it yet.
- 3. Run git checkout on the hash I've provided.
- In both scenarios, the last step is to checkout the commit (using the provided hash). That updates
- your working directory (the code you actually work on) with the version present in that commit.
- To do the checkout, run `git checkout <commit>`, where '<commit>' is the hash I've provided. For
- this chapter, the hash is 9c3a2e731ec03644bd7f0c92cca3e91d4d04aec4.
- You may have changes in your working copy. Sometimes Git can cleverly merge them, but often it will
- complain that a checkout can't be done because it would overwrite those files. Git also doesn't
- care about files that you added but didn't include in any commit yet. Because of this, it's good
- practice to run `git status` after a checkout to verify that the repo is clean.
- If your local copy is screwed up and you can't get the checkout command to work, the easiest way to
- fix it is by simply deleting the entire repository and just re-cloning (ie, following the
- instructions for scenario 1). Nuke and restart is generally not a great practice, but since Git can
- be quite complicated and we've only covered the very basics, it's perfectly forgivable to sidestep
- all that and keeping it simple.
- Section II: write a basic main function
- ---------------------------------------
- A good first step to starting any new project is to start with the simplest possible starting
- point. As such, we'll write a quick main function that doesn't do anything yet, but compiles
- properly.
- The absolute base requirements for any functioning C program is simply that main is present. So you
- could start with just this code:
- - main.c ---
- int main(){}
- ------------
- However, in the interest of completeness, let's make sure it can take arguments and also returns
- success back to the operating system. Always keep it clean!
- --------------------- main.c ---
- int main (int argc, char** argv)
- {
- return 0;
- }
- --------------------------------
- Compile and run it. This is your sanity check - if compilation fails on a basic program like this,
- you either screwed up, or something is wrong with your system. Don't continue until compilation
- works, the problem will not magically disappear.
- Section III: finding SDL2 documentation
- ---------------------------------------
- SDL2 is a product for programmers, and as such it has a manual. Manuals come in many shapes and
- sizes. For SDL, the easiest to use ones are the man pages, and the wiki.
- * Wiki
- URL: https://wiki.libsdl.org/
- The wiki is the easiest to use when you're online, and can be used from any system. It also has
- the benefit of having tutorials, easy to use navigation, and a search bar.
- Try, for example, searching for SDL_Delay, and you'll be brought to this page:
- https://wiki.libsdl.org/SDL_Delay
- * Linux man pages
- This requires that you have SDL2 actually installed (and some distributions may package the man
- pages separately), but is by far the fastest way to get information.
- Just run `man` followed by any SDL function you want to know about (example: `man SDL_Delay`) to
- get the information for that function.
- Both approaches will give you the information you need for anything you use in SDL2. For functions,
- that means it'll give you a list of what the function does, what it returns, and what you need to
- pass it.
- Beyond the wiki and man pages, there's also plenty of tutorials and support forums. Whenever you
- have a problem that's not covered here, try just describing it in a search engine, and you'll almost
- always find someone who had a similar problem, and some potential solutions to try.
- Section IV: waiting with SDL_Delay
- ----------------------------------
- SDL_Delay is one of the simplest functions you can use in SDL2. It's a function that does nothing
- but wait a certain amount of time. It sounds useless, but it's used in almost every SDL program.
- Open the documentation here, and I'll walk you through it:
- https://wiki.libsdl.org/SDL_Delay
- You'll see that the syntax is described as such:
- -------------------------
- void SDL_Delay(Uint32 ms)
- -------------------------
- As a reminder - that means the function name is SDL_Delay, it returns nothing (void), and requires
- milliseconds as an input. You're not yet familiar with Uint32. It's a custom type defined by SDL2
- itself. You'll learn how to make custom types yourself and that'll help you understand this. For
- now, just know that you can pass any positive number to it.
- The documentation will tell you what you need to know - SDL_Delay just waits the specified amount
- of milliseconds. Try updating your code to look something like this:
- ===================== main.c ===
- #include <SDL2/SDL_timer.h>
- int main (int argc, char** argv)
- {
- SDL_Delay(3000);
- return 0;
- }
- ================================
- This won't compile yet - you'll see a linker error. We'll fix that in the next section.
- Like any other function, SDL_Delay can be called anywhere in your program, so long as it's declared
- in advance. SDL2 organizes all its header files into the SDL2 subfolder.
- On most systems, all standard header files can be found in /usr/include. That includes the ones for
- the standard C library - take a look at stdio.h, which provides printf for you. There's also quite
- a few subfolders in there, depending on what you have installed on your system.
- SDL2 has a subfolder in there: /usr/include/SDL2. Try to list the files in there, and you'll see
- that SDL_timer.h is in there as well. When gcc searches for header files, it checks a couple
- places, including /usr/include. However, it doesn't check subfolders. So when you want to include
- a file from this subfolder, you must specify the subfolder as well.
- Hence, the line `#include <SDL2/SDL_timer.h` really results in the compiler including the content
- from /usr/include/SDL2/SDL_timer.h.
- By the way, you can also choose to include SDL2/SDL.h instead, which in turns includes all the
- other files. It's just a little convenience to make things easier for the programmer. With this
- approach you'll include a lot of definitions you don't need, but the references that aren't used
- are lost when the compiler turns the code into Assembly, so the only real consequence is that it
- takes every so slightly longer to build - a consequence no one really cares about.
- Section V: linking to SDL2
- --------------------------
- Trying to compile the code you added will result in a linker error:
- ----------------------------------- [ gcc output ] ---
- [cuterald $] gcc main.c
- /usr/bin/ld: /tmp/ccWVVpwe.o: in function `main':
- main.c:(.text+0x15): undefined reference to `SDL_Delay'
- collect2: error: ld returned 1 exit status
- -------------------------------------------------------
- This is because the actual implementation for SDL_Delay isn't in your program, and it's not in the
- C standard library either. You'll have to tell the compiler where to actually find it.
- Like with includes, GCC has a couple default places where it'll check for any library you may be
- trying to link to. So all you really have to do is pass it the name, and if SDL2 is installed
- properly, it will find it automatically.
- Compile it with this:
- --------- [ gcc command ] ---
- gcc -lSDL2 main.c -o cuterald
- -----------------------------
- This time the program will compile without errors. This is all you really need to know to compile
- any SDL2 program from now on, but as usual we'll go just a bit deeper so you can get a full
- understanding.
- GCC comes with a utility to check what an executable is linked to, called 'ldd'. Run it on any
- executable file, and it'll tell you exactly what it's linked to.
- For example:
- -------------------------------------------------------------------- [ ldd output ] ---
- [cuterald $] ldd cuterald
- linux-vdso.so.1 (0x00007fff87dd2000)
- libSDL2-2.0.so.0 => /usr/lib/libSDL2-2.0.so.0 (0x00007fa9dcbd8000)
- libc.so.6 => /usr/lib/libc.so.6 (0x00007fa9dca0f000)
- libm.so.6 => /usr/lib/libm.so.6 (0x00007fa9dc8c9000)
- libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fa9dc8c3000)
- libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fa9dc8a1000)
- /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fa9dcdb4000)
- ---------------------------------------------------------------------------------------
- This shows you that gcc links to a couple standard libraries by default (which is why you can use
- printf and other default functions without having to manually link), and SDL is in there as well.
- From this output, you can learn that the SDL2 implementation really sits in
- /usr/lib/libSDL2-2.0.so.0
- That's a .o file - the result of the assembly stage in GCC, and the binary format that gets used as
- input for the linker stage. When you pass the -l flag for GCC, this file is the one that is used
- during linking to find the implementation for any SDL2 function.
- Section VI: chapter summary
- ---------------------------
- Your program doesn't really do anything yet. It just waits the amount of milliseconds that you
- passed to SDL_Delay, and then quits. The important part is that it runs at all - using and linking
- to external libraries is one of those things that have a steep learning cure, but are very easy
- once you understand how it works.
- In summary, this is what we learned today (and the last chapter):
- * SDL2 provides a number of features to make game development easy
- * Like most libraries, you need to #include the necessary files, and also link to the actual binary
- library file.
- * Includes are usually in /usr/include/ and subfolders.
- * Library files are usually in /usr/lib
- * Any library can be linked to using the -l flag.
- * Failing to provide the library name will result in a linker error.
- * SDL2 has documentation online at https://wiki.libsdl.org/, as well as in the Linux man pages.
- * SDL_Delay waits a given amount of milliseconds, then returns.
- * ldd can show you what libraries a binary is linked to.
- Don't forget to git add and commit your code!
|