Here is a small guide for how to make Atom cooperate with SFML.
I am writing this because while I could find very limited information on making the combination work, it was never for a Mac environment. And so after some experimenting I found a solution that seems to work.
First off, Atom is just a text editor. It never claims to be more. But the beauty of Atom is in the range of plugins available, making it easy to build it into an IDE if you wish. Introducing:
Script is a wonderful package for Atom that adds compilers for a huge number of languages. You can check out the GitHub page for more info.
We will be using Script as our compiler interface to make SFML work within Atom. Similar techniques are probably possible with other C++ compiler packages, but I don't have any experience with those.
- Download Script with
apm install script
or directly from inside Atom. See their page for more. - Script compiles C++ with
xcrun
andclang++
. Make sure you have these, or install them. - Download SFML for MacOS from here. Go ahead and decompress the file.
- Navigate to
/usr/local/Frameworks/
(You can see hidden files and folders withcommand shift .
) - From inside the decompressed SFML folder, copy everything from
extlibs
andFrameworks
into/usr/local/Frameworks/
. ~Note: I'm not 100% sure whether the extra libraries are working this way. Let me know if they don't. - Everything from
/SFML-x.x.x/include/
needs to go in/usr/local/include/
- And finally everything from
/SFML-x.x.x/lib/
goes into/usr/local/lib/
You don't need any of the other files, but some (especially Templates) might be useful as references later.
While Script comes with an assortment of run options, I couldn't find a way use them to compile and open my SFML project in one command.
Potentially someone smarter could work out how to use the run options more effectively, however my solution just edits the Script source files to automatically read my main.cpp, and include the SFML object files if it finds any of the SFML headers.
I also added a code snippet to have it save the compiled app to my project directory, rather than a temp file. However that is just my own preference.
At the moment SFML headers have to be included in main.cpp or not at all. Using the run options of Script may also not work. I might try solve these at later date but I'm happy for now.
Anyway, on to the code:
- The file we are interested in is:
/Users/You/.atom/packages/script/lib/grammars/c.coffee
- First we add the main command arguments as one variable. Paste the following somewhere near the top of the file (I put it straight under 'options'):
#path = require 'path'
#{OperatingSystem, command} = GrammarUtils = require '../grammar-utils'
#os = OperatingSystem.platform()
#windows = OperatingSystem.isWindows()
#options = '-Wall -include stdio.h'
sfmloptions = ' -framework sfml-graphics -framework sfml-audio -framework sfml-network -framework sfml-system -framework sfml-window -F /usr/local/Frameworks'
- Next, we are only interested in the 'C++' section of the code. We will edit both selection based and file based commands, so that we can use both. Edit the code (around line 80) to look like the following: (I have commented out unchanged lines).
# exports['C++'] =
# 'Selection Based':
# command: 'bash'
# args: (context) ->
# code = context.getCode()
if code.indexOf("#include <SFML/Graphics.hpp>") != -1 || code.indexOf("#include <SFML/Audio.hpp>") != -1 || code.indexOf("#include <SFML/Network.hpp>") != -1 || code.indexOf("#include <SFML/System.hpp>") != -1 || code.indexOf("#include <SFML/Window.hpp>") != -1
options += sfmloptions
# tmpFile = GrammarUtils.createTempFileWithCode(code, '.cpp')
# args = switch os
# when 'darwin'
# "xcrun clang++ -std=c++14 #{options} -fcolor-diagnostics -include iostream #{tmpFile} -o /tmp/cpp.out && /tmp/cpp.out"
# when 'linux'
# "g++ #{options} -std=c++14 -include iostream #{tmpFile} -o /tmp/cpp.out && /tmp/cpp.out"
# return ['-c', args]
# 'File Based': {
# command
# args: ({filepath}) ->
fs = require 'fs'
getCodeFromFile = ->
fs.readFileSync filepath, 'utf8'
code = getCodeFromFile()
if code.indexOf("#include <SFML/Graphics.hpp>") != -1 || code.indexOf("#include <SFML/Audio.hpp>") != -1 || code.indexOf("#include <SFML/Network.hpp>") != -1 || code.indexOf("#include <SFML/System.hpp>") != -1 || code.indexOf("#include <SFML/Window.hpp>") != -1
options += sfmloptions
# args = switch os
# when 'darwin'
# "xcrun clang++ -std=c++14 #{options} -fcolor-diagnostics -include iostream '#{filepath}' -o /tmp/cpp.out && /tmp/cpp.out"
# when 'linux'
# "g++ -std=c++14 #{options} -include iostream '#{filepath}' -o /tmp/cpp.out && /tmp/cpp.out"
# when 'win32'
# if GrammarUtils.OperatingSystem.release().split('.').slice -1 >= '14399'
# filepath = path.posix.join.apply(path.posix, [].concat([filepath.split(path.win32.sep)[0].toLowerCase()], filepath.split(path.win32.sep).slice(1))).replace(':', '')
# "g++ -std=c++14 #{options} -include iostream /mnt/#{filepath} -o /tmp/cpp.out && /tmp/cpp.out"
# return GrammarUtils.formatArgs(args)
# }
And that should do it! Your SFML code should now be running, or almost. The following code bits are for quality of life mostly.
If you first try to run the example program that SFML provides in the templates folder, you will find that the function resourcePath()
raises an error (undefined).
This is purely a helper function, and it's possible to write programs simply by providing the full path to your resources. If you still need it however, we can write our own small function using the library <filesystem>
introduced in C++17.
- First make sure that your clang version is at least version 5
clang --version
- Edit
c.coffee
again, to switch to c++17 (the default is c++14) Edit both file mode and selection mode:
# args = switch os
# when 'darwin'
"xcrun clang++ -std=c++17 #{options} -fcolor-diagnostics -include iostream #{tmpFile} -o /tmp/cpp.out && /tmp/cpp.out"
# when 'linux'
# "g++ #{options} -std=c++14 -include iostream #{tmpFile} -o /tmp/cpp.out && /tmp/cpp.out"
- Now we can define resourcePath() wherever we need it.
#include <filesystem>
std::string resourcePath() {
return std::string(std::filesystem::current_path()) + "/";
}
If, like me, you rather have your compiled app right in your working directory rather than in a temp folder, change the following (again in c.coffee
). Note, my version will only change if I am working on an SFML project, and only in file mode. You can edit as you wish.
#if code.indexOf("#include <SFML/Graphics.hpp>") != -1 || code.indexOf("#include <SFML/Audio.hpp>") != -1 || code.indexOf("#include <SFML/Network.hpp>") != -1 || code.indexOf("#include <SFML/System.hpp>") != -1 || code.indexOf("#include <SFML/Window.hpp>") != -1
# options += sfmloptions
dir = '/path/to/directory/myapp.app'
#args = switch os
# when 'darwin'
"xcrun clang++ -std=c++17 #{options} -fcolor-diagnostics -include iostream '#{filepath}' -o #{dir} && #{dir}"
# when 'linux'
Finally, while your code may be running smoothly now, there is a chance that your linter may be greatly disagreeing with all these changes. I use linter-gcc but similar steps probably work for other linters.
-
In linter-gcc settings add
-std=c++17
to your C++ Flags. -
Linters using a homebrew installation of gcc won't be able to see the SFML header files. I found the easiest fix was to copy the SFML header folder inside to:
/usr/local/Cellar/gcc/x.x.x/include/c++/x.x.x/
- You could probably also just pass the header directory to gcc as an argument but I couldn't get that to work for some reason.
If you have other questions or issues or i forgot something or made a mistake somewhere, just let me know. This is my first 'guide' so there's bound to be errors. Anyway happy coding!
- Keldan