├── .gitignore ├── Cargo.toml ├── .travis.yml ├── src └── sdl2_ttf │ ├── lib.rs │ ├── context.rs │ ├── ffi.rs │ └── font.rs ├── LICENSE ├── CHANGELOG.md ├── README.md ├── examples └── demo.rs └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /bin/main 15 | /bin/test-internal 16 | /bin/test-external 17 | /doc/ 18 | /target/ 19 | /build/ 20 | /.rust/ 21 | rusti.sh 22 | Cargo.lock 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sdl2_ttf" 3 | description = "SDL2_ttf bindings for Rust" 4 | repository = "https://github.com/andelf/rust-sdl2_ttf" 5 | version = "0.25.1" 6 | license = "MIT" 7 | readme = "README.md" 8 | authors = ["ShuYu Wang "] 9 | keywords = ["SDL", "windowing", "graphics", "font","ttf"] 10 | 11 | [lib] 12 | name = "sdl2_ttf" 13 | path = "src/sdl2_ttf/lib.rs" 14 | 15 | [dependencies] 16 | bitflags = "0.6" 17 | sdl2 = "0.25" 18 | sdl2-sys = "0.25" 19 | 20 | # [dependencies.sdl2] 21 | # git = "https://github.com/AngryLawyer/rust-sdl2/" 22 | 23 | # [dependencies.sdl2-sys] 24 | # git = "https://github.com/AngryLawyer/rust-sdl2/" 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - beta 4 | - stable 5 | env: 6 | global: 7 | - secure: bEpP3zUNn1g3jOhB9s0y8XDbxtAcf8rt2ilwkx6/OjhZm7mcfcvjoiEyG/c6iu4AOhF3CTvhnjZmuuD7d1eUChNwWpNhyWMZ3gj+lNNAugfZfOv7sFFQpEMZ2hbO1djr7/TeuXmQgJqIUHYfCoW2nXObXkESpl2yzOiYhhUdH58= 8 | - LD_LIBRARY_PATH: /usr/local/lib 9 | install: 10 | - cd .. 11 | - time wget -q http://www.libsdl.org/release/SDL2-2.0.3.tar.gz 12 | - time wget -q http://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.12.tar.gz 13 | - time tar xf SDL2-*.tar.gz 14 | - time tar xf SDL2_ttf-*.tar.gz 15 | - cd SDL2-* 16 | - ./configure && make && sudo make install 17 | - cd - 18 | - cd SDL2_ttf-* 19 | - ./configure && make && sudo make install 20 | - cd - 21 | script: 22 | - cd rust-sdl2_ttf 23 | - cargo build -v 24 | - cargo test -v 25 | - cargo doc -v 26 | after_script: 27 | - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh 28 | -------------------------------------------------------------------------------- /src/sdl2_ttf/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | A binding for `SDL2_ttf`. 3 | */ 4 | 5 | extern crate sdl2; 6 | extern crate sdl2_sys; 7 | 8 | #[macro_use] 9 | extern crate bitflags; 10 | 11 | #[allow(non_camel_case_types, dead_code)] 12 | mod ffi; 13 | mod font; 14 | mod context; 15 | 16 | // Setup linking for all targets. 17 | #[cfg(target_os="macos")] 18 | mod mac { 19 | #[cfg(mac_framework)] 20 | #[link(kind="framework", name="SDL2_ttf")] 21 | extern {} 22 | 23 | #[cfg(not(mac_framework))] 24 | #[link(name="SDL2_ttf")] 25 | extern {} 26 | } 27 | 28 | #[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))] 29 | mod others { 30 | #[link(name="SDL2_ttf")] 31 | extern {} 32 | } 33 | 34 | pub use context::{ 35 | init, has_been_initialized, get_linked_version, Sdl2TtfContext, InitError, 36 | }; 37 | pub use font::{ 38 | Font, FontStyle, Hinting, GlyphMetrics, PartialRendering, FontError, 39 | FontResult, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, STYLE_UNDERLINE, STYLE_STRIKETHROUGH 40 | }; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 ShuYu Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## 0.14.0 3 | ### Changes 4 | - Added a changelog 5 | - The ttf context is now needed to create fonts, which should make it more apparent that it needs to live 6 | - Changed some i32 indices to u16 (mainly widths, where subzero should be represented with an ```Option``` anyway) 7 | - Moved font creation to the TTF context 8 | - Changed the render method to use a builder pattern 9 | - Updated the demo example to work with the above changes 10 | - The order of arguments when loading indexed fonts was changed, to first select the font/index and then its size 11 | - The functions of the 'load from RWops' extension trait was moved to the TTF context too 12 | - ```Font.size``` has been split into ```Font.size_of``` (for UTF-8), ```Font.size_of_latin1``` (for Latin-1) and ```Font.size_of_char``` 13 | - ```Font.render``` has been split into ```Font.render``` (for UTF-8), ```Font.render_latin1``` (for Latin-1) and ```Font.render_char``` 14 | - The font size checks should now properly use UTF-8. (It used to rely on the Latin-1 function) 15 | 16 | ### Renames 17 | - ```Font.get_outline``` -> ```Font.get_outline_width``` 18 | - ```Font.set_outline``` -> ```Font.set_outline_width``` 19 | - ```Font.line_skip``` -> ```Font.recommended_line_spacing``` 20 | - ```Font.faces``` -> ```Font.face_count``` 21 | - ```Font.index_of_char``` -> ```Font.find_glyph``` 22 | - ```Font.metrics_of_char``` -> ```Font.find_glyph_metrics``` 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [As of rust-sdl2v0.27 this crate is deprecated and its functionality has been moved as a feature in rust-sdl2](https://github.com/AngryLawyer/rust-sdl2) 2 | 3 | Rust-SDL2_ttf 4 | ============= 5 | 6 | [![Build Status](https://travis-ci.org/andelf/rust-sdl2_ttf.svg?branch=master)](https://travis-ci.org/andelf/rust-sdl2_ttf) 7 | [![crates.io](http://meritbadge.herokuapp.com/sdl2_ttf)](https://crates.io/crates/sdl2_ttf) 8 | 9 | Rust bindings for SDL2_ttf. 10 | 11 | ## Overview 12 | 13 | Rust-SDL2_ttf is a library for talking to the new SDL2_ttf library from Rust. 14 | 15 | Rust-SDL2_ttf uses the MIT licence. 16 | 17 | ## [Documentation](https://docs.rs/sdl2_ttf/0.25.0/sdl2_ttf/) 18 | 19 | ## Requirements 20 | 21 | * [Rust-SDL2](https://github.com/AngryLawyer/rust-sdl2) 22 | * SDL2_ttf development libraries 23 | * Rust master or nightly 24 | 25 | ## Installation 26 | 27 | Place the following into your project's Cargo.toml file: 28 | 29 | ```toml 30 | [dependencies] 31 | sdl2_ttf = "0.25" 32 | ``` 33 | 34 | sdl2_ttf is directly compatible with the corresponding version of sdl2. 35 | Hence sdl2_ttf v0.15 is compatible with sdl2 0.15, and so forth. 36 | Backwards compatibility is not guaranteed by rust-sdl2, so take that into 37 | account when creating new projects ! 38 | 39 | If you want the newest rust-sdl2_ttf, reference the repository: 40 | 41 | ```toml 42 | [dependencies.sdl2_ttf] 43 | git = "https://github.com/andelf/rust-sdl2_ttf" 44 | ``` 45 | 46 | You can also just clone and build the library yourself: 47 | 48 | ```bash 49 | git clone https://github.com/andelf/rust-sdl2_ttf 50 | cd rust-sdl2_ttf 51 | cargo build 52 | # TODO: OR if you are using the mac framework version 53 | rustc -L. --cfg mac_framework src/sdl2_ttf/lib.rs 54 | ``` 55 | 56 | If you're not using Cargo, you can compile the library manually: 57 | 58 | ```bash 59 | git clone https://github.com/andelf/rust-sdl2_ttf 60 | cd rust-sdl2_ttf 61 | rustc src/sdl2_ttf/lib.rs 62 | ``` 63 | 64 | ## Demo 65 | 66 | A simple demo that prints out a string given a font is included: 67 | 68 | ```bash 69 | cargo run --example demo /path/to/font.(ttf|ttc|fon) 70 | ``` 71 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | extern crate sdl2; 2 | extern crate sdl2_ttf; 3 | 4 | use std::env; 5 | use std::path::Path; 6 | 7 | use sdl2::event::Event; 8 | use sdl2::keyboard::Keycode; 9 | use sdl2::rect::Rect; 10 | use sdl2::render::TextureQuery; 11 | use sdl2::pixels::Color; 12 | 13 | static SCREEN_WIDTH : u32 = 800; 14 | static SCREEN_HEIGHT : u32 = 600; 15 | 16 | // handle the annoying Rect i32 17 | macro_rules! rect( 18 | ($x:expr, $y:expr, $w:expr, $h:expr) => ( 19 | Rect::new($x as i32, $y as i32, $w as u32, $h as u32) 20 | ) 21 | ); 22 | 23 | // Scale fonts to a reasonable size when they're too big (though they might look less smooth) 24 | fn get_centered_rect(rect_width: u32, rect_height: u32, cons_width: u32, cons_height: u32) -> Rect { 25 | let wr = rect_width as f32 / cons_width as f32; 26 | let hr = rect_height as f32 / cons_height as f32; 27 | 28 | let (w, h) = if wr > 1f32 || hr > 1f32 { 29 | if wr > hr { 30 | println!("Scaling down! The text will look worse!"); 31 | let h = (rect_height as f32 / wr) as i32; 32 | (cons_width as i32, h) 33 | } else { 34 | println!("Scaling down! The text will look worse!"); 35 | let w = (rect_width as f32 / hr) as i32; 36 | (w, cons_height as i32) 37 | } 38 | } else { 39 | (rect_width as i32, rect_height as i32) 40 | }; 41 | 42 | let cx = (SCREEN_WIDTH as i32 - w) / 2; 43 | let cy = (SCREEN_HEIGHT as i32 - h) / 2; 44 | rect!(cx, cy, w, h) 45 | } 46 | 47 | fn run(font_path: &Path) { 48 | let sdl_context = sdl2::init().unwrap(); 49 | let video_subsys = sdl_context.video().unwrap(); 50 | let ttf_context = sdl2_ttf::init().unwrap(); 51 | 52 | let window = video_subsys.window("SDL2_TTF Example", SCREEN_WIDTH, SCREEN_HEIGHT) 53 | .position_centered() 54 | .opengl() 55 | .build() 56 | .unwrap(); 57 | 58 | let mut renderer = window.renderer().build().unwrap(); 59 | 60 | // Load a font 61 | let mut font = ttf_context.load_font(font_path, 128).unwrap(); 62 | font.set_style(sdl2_ttf::STYLE_BOLD); 63 | 64 | // render a surface, and convert it to a texture bound to the renderer 65 | let surface = font.render("Hello Rust!") 66 | .blended(Color::RGBA(255, 0, 0, 255)).unwrap(); 67 | let mut texture = renderer.create_texture_from_surface(&surface).unwrap(); 68 | 69 | renderer.set_draw_color(Color::RGBA(195, 217, 255, 255)); 70 | renderer.clear(); 71 | 72 | let TextureQuery { width, height, .. } = texture.query(); 73 | 74 | // If the example text is too big for the screen, downscale it (and center irregardless) 75 | let padding = 64; 76 | let target = get_centered_rect(width, height, SCREEN_WIDTH - padding, SCREEN_HEIGHT - padding); 77 | 78 | renderer.copy(&mut texture, None, Some(target)).unwrap(); 79 | renderer.present(); 80 | 81 | 'mainloop: loop { 82 | for event in sdl_context.event_pump().unwrap().poll_iter() { 83 | match event { 84 | Event::Quit{..} => break 'mainloop, 85 | Event::KeyDown {keycode: Some(Keycode::Escape), ..} => break 'mainloop, 86 | _ => {} 87 | } 88 | } 89 | } 90 | } 91 | 92 | fn main() { 93 | let args: Vec<_> = env::args().collect(); 94 | 95 | println!("linked sdl2_ttf: {}", sdl2_ttf::get_linked_version()); 96 | 97 | if args.len() < 2 { 98 | println!("Usage: ./demo font.[ttf|ttc|fon]") 99 | } else { 100 | let path: &Path = Path::new(&args[1]); 101 | run(path); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/sdl2_ttf/context.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::error; 3 | use std::fmt; 4 | use std::os::raw::{c_int, c_long}; 5 | use std::path::Path; 6 | use sdl2::get_error; 7 | use sdl2::rwops::RWops; 8 | use sdl2::version::Version; 9 | 10 | use font::{ 11 | internal_load_font, 12 | internal_load_font_at_index, 13 | internal_load_font_from_ll, 14 | Font, 15 | }; 16 | 17 | use ffi; 18 | 19 | /// A context manager for `SDL2_TTF` to manage C code initialization and clean-up. 20 | #[must_use] 21 | pub struct Sdl2TtfContext; 22 | 23 | // Clean up the context once it goes out of scope 24 | impl Drop for Sdl2TtfContext { 25 | fn drop(&mut self) { 26 | unsafe { ffi::TTF_Quit(); } 27 | } 28 | } 29 | 30 | impl Sdl2TtfContext { 31 | /// Loads a font from the given file with the given size in points. 32 | pub fn load_font<'a>(&'a self, path: &'a Path, point_size: u16) -> Result { 33 | internal_load_font(path, point_size) 34 | } 35 | 36 | /// Loads the font at the given index of the file, with the given 37 | /// size in points. 38 | pub fn load_font_at_index<'a>(&'a self, path: &'a Path, index: u32, point_size: u16) 39 | -> Result { 40 | internal_load_font_at_index(path, index, point_size) 41 | } 42 | 43 | /// Loads a font from the given SDL2 rwops object with the given size in 44 | /// points. 45 | pub fn load_font_from_rwops<'a,'b>(&'a self, rwops: RWops<'b>, point_size: u16) 46 | -> Result, String> { 47 | let raw = unsafe { 48 | ffi::TTF_OpenFontRW(rwops.raw(), 0, point_size as c_int) 49 | }; 50 | if (raw as *mut ()).is_null() { 51 | Err(get_error()) 52 | } else { 53 | Ok(internal_load_font_from_ll(raw, Some(rwops))) 54 | } 55 | } 56 | 57 | /// Loads the font at the given index of the SDL2 rwops object with 58 | /// the given size in points. 59 | pub fn load_font_at_index_from_rwops<'a,'b>(&'a self, rwops: RWops<'b>, index: u32, 60 | point_size: u16) -> Result, String> { 61 | let raw = unsafe { 62 | ffi::TTF_OpenFontIndexRW(rwops.raw(), 0, point_size as c_int, 63 | index as c_long) 64 | }; 65 | if (raw as *mut ()).is_null() { 66 | Err(get_error()) 67 | } else { 68 | Ok(internal_load_font_from_ll(raw, Some(rwops))) 69 | } 70 | } 71 | } 72 | 73 | /// Returns the version of the dynamically linked `SDL_TTF` library 74 | pub fn get_linked_version() -> Version { 75 | unsafe { 76 | Version::from_ll(*ffi::TTF_Linked_Version()) 77 | } 78 | } 79 | 80 | /// An error for when `sdl2_ttf` is attempted initialized twice 81 | /// Necessary for context management, unless we find a way to have a singleton 82 | #[derive(Debug)] 83 | pub enum InitError { 84 | InitializationError(io::Error), 85 | AlreadyInitializedError, 86 | } 87 | 88 | impl error::Error for InitError { 89 | fn description(&self) -> &str { 90 | match *self { 91 | InitError::AlreadyInitializedError => { 92 | "SDL2_TTF has already been initialized" 93 | }, 94 | InitError::InitializationError(ref error) => { 95 | error.description() 96 | }, 97 | } 98 | } 99 | 100 | fn cause(&self) -> Option<&error::Error> { 101 | match *self { 102 | InitError::AlreadyInitializedError => { 103 | None 104 | }, 105 | InitError::InitializationError(ref error) => { 106 | Some(error) 107 | }, 108 | } 109 | } 110 | } 111 | 112 | impl fmt::Display for InitError { 113 | fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { 114 | formatter.write_str("SDL2_TTF has already been initialized") 115 | } 116 | } 117 | 118 | /// Initializes the truetype font API and returns a context manager which will 119 | /// clean up the library once it goes out of scope. 120 | pub fn init() -> Result { 121 | unsafe { 122 | if ffi::TTF_WasInit() == 1 { 123 | Err(InitError::AlreadyInitializedError) 124 | } else if ffi::TTF_Init() == 0 { 125 | Ok(Sdl2TtfContext) 126 | } else { 127 | Err(InitError::InitializationError( 128 | io::Error::last_os_error() 129 | )) 130 | } 131 | } 132 | } 133 | 134 | /// Returns whether library has been initialized already. 135 | pub fn has_been_initialized() -> bool { 136 | unsafe { 137 | ffi::TTF_WasInit() == 1 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/sdl2_ttf/ffi.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_int, c_char, c_long, c_void}; 2 | use sdl2_sys::surface::SDL_Surface; 3 | use sdl2_sys::pixels::SDL_Color; 4 | use sdl2_sys::rwops::SDL_RWops; 5 | use sdl2_sys::version::SDL_version; 6 | 7 | 8 | pub const TTF_STYLE_NORMAL : c_int = 0x00; 9 | pub const TTF_STYLE_BOLD : c_int = 0x01; 10 | pub const TTF_STYLE_ITALIC : c_int = 0x02; 11 | pub const TTF_STYLE_UNDERLINE : c_int = 0x04; 12 | pub const TTF_STYLE_STRIKETHROUGH : c_int = 0x08; 13 | 14 | pub const TTF_HINTING_NORMAL : c_int = 0; 15 | pub const TTF_HINTING_LIGHT : c_int = 1; 16 | pub const TTF_HINTING_MONO : c_int = 2; 17 | pub const TTF_HINTING_NONE : c_int = 3; 18 | 19 | pub type TTF_Font = c_void; 20 | extern "C" { 21 | pub fn TTF_Linked_Version() -> *const SDL_version; 22 | pub fn TTF_ByteSwappedUNICODE(swapped: c_int); 23 | pub fn TTF_Init() -> c_int; 24 | pub fn TTF_OpenFont(file: *const c_char, ptsize: c_int) -> *const TTF_Font; 25 | pub fn TTF_OpenFontIndex(file: *const c_char, ptsize: c_int, index: c_long) -> 26 | *const TTF_Font; 27 | pub fn TTF_OpenFontRW(src: *const SDL_RWops, freesrc: c_int, ptsize: c_int) 28 | -> *const TTF_Font; 29 | pub fn TTF_OpenFontIndexRW(src: *const SDL_RWops, freesrc: c_int, 30 | ptsize: c_int, index: c_long) -> *const TTF_Font; 31 | pub fn TTF_GetFontStyle(font: *const TTF_Font) -> c_int; 32 | pub fn TTF_SetFontStyle(font: *const TTF_Font, style: c_int); 33 | pub fn TTF_GetFontOutline(font: *const TTF_Font) -> c_int; 34 | pub fn TTF_SetFontOutline(font: *const TTF_Font, outline: c_int); 35 | pub fn TTF_GetFontHinting(font: *const TTF_Font) -> c_int; 36 | pub fn TTF_SetFontHinting(font: *const TTF_Font, hinting: c_int); 37 | pub fn TTF_FontHeight(font: *const TTF_Font) -> c_int; 38 | pub fn TTF_FontAscent(font: *const TTF_Font) -> c_int; 39 | pub fn TTF_FontDescent(font: *const TTF_Font) -> c_int; 40 | pub fn TTF_FontLineSkip(font: *const TTF_Font) -> c_int; 41 | pub fn TTF_GetFontKerning(font: *const TTF_Font) -> c_int; 42 | pub fn TTF_SetFontKerning(font: *const TTF_Font, allowed: c_int); 43 | pub fn TTF_FontFaces(font: *const TTF_Font) -> c_long; 44 | pub fn TTF_FontFaceIsFixedWidth(font: *const TTF_Font) -> c_int; 45 | pub fn TTF_FontFaceFamilyName(font: *const TTF_Font) -> *const c_char; 46 | pub fn TTF_FontFaceStyleName(font: *const TTF_Font) -> *const c_char; 47 | pub fn TTF_GlyphIsProvided(font: *const TTF_Font, ch: u16) -> c_int; 48 | pub fn TTF_GlyphMetrics(font: *const TTF_Font, ch: u16, minx: *const c_int, 49 | maxx: *const c_int, miny: *const c_int, 50 | maxy: *const c_int, advance: *const c_int) -> c_int; 51 | pub fn TTF_SizeText(font: *const TTF_Font, text: *const c_char, w: *const c_int, 52 | h: *const c_int) -> c_int; 53 | pub fn TTF_SizeUTF8(font: *const TTF_Font, text: *const c_char, w: *const c_int, 54 | h: *const c_int) -> c_int; 55 | pub fn TTF_SizeUNICODE(font: *const TTF_Font, text: *const u16, w: *const c_int, 56 | h: *const c_int) -> c_int; 57 | pub fn TTF_RenderText_Solid(font: *const TTF_Font, text: *const c_char, 58 | fg: SDL_Color) -> *mut SDL_Surface; 59 | pub fn TTF_RenderUTF8_Solid(font: *const TTF_Font, text: *const c_char, 60 | fg: SDL_Color) -> *mut SDL_Surface; 61 | pub fn TTF_RenderUNICODE_Solid(font: *const TTF_Font, text: *const u16, 62 | fg: SDL_Color) -> *mut SDL_Surface; 63 | pub fn TTF_RenderGlyph_Solid(font: *const TTF_Font, ch: u16, 64 | fg: SDL_Color) -> *mut SDL_Surface; 65 | pub fn TTF_RenderText_Shaded(font: *const TTF_Font, text: *const c_char, 66 | fg: SDL_Color, bg: SDL_Color) -> 67 | *mut SDL_Surface; 68 | pub fn TTF_RenderUTF8_Shaded(font: *const TTF_Font, text: *const c_char, 69 | fg: SDL_Color, bg: SDL_Color) -> 70 | *mut SDL_Surface; 71 | pub fn TTF_RenderUNICODE_Shaded(font: *const TTF_Font, text: *const u16, 72 | fg: SDL_Color, bg: SDL_Color) -> 73 | *mut SDL_Surface; 74 | pub fn TTF_RenderGlyph_Shaded(font: *const TTF_Font, ch: u16, 75 | fg: SDL_Color, bg: SDL_Color) -> 76 | *mut SDL_Surface; 77 | pub fn TTF_RenderText_Blended(font: *const TTF_Font, text: *const c_char, 78 | fg: SDL_Color) -> *mut SDL_Surface; 79 | pub fn TTF_RenderUTF8_Blended(font: *const TTF_Font, text: *const c_char, 80 | fg: SDL_Color) -> *mut SDL_Surface; 81 | pub fn TTF_RenderUNICODE_Blended(font: *const TTF_Font, text: *const u16, 82 | fg: SDL_Color) -> *mut SDL_Surface; 83 | pub fn TTF_RenderText_Blended_Wrapped(font: *const TTF_Font, text: *const c_char, 84 | fg: SDL_Color, wrapLength: u32) 85 | -> *mut SDL_Surface; 86 | pub fn TTF_RenderUTF8_Blended_Wrapped(font: *const TTF_Font, text: *const c_char, 87 | fg: SDL_Color, wrapLength: u32) 88 | -> *mut SDL_Surface; 89 | pub fn TTF_RenderUNICODE_Blended_Wrapped(font: *const TTF_Font, 90 | text: *const u16, fg: SDL_Color, 91 | wrapLength: u32) -> 92 | *const SDL_Surface; 93 | pub fn TTF_RenderGlyph_Blended(font: *const TTF_Font, ch: u16, 94 | fg: SDL_Color) -> *mut SDL_Surface; 95 | pub fn TTF_CloseFont(font: *const TTF_Font); 96 | pub fn TTF_Quit(); 97 | pub fn TTF_WasInit() -> c_int; 98 | pub fn TTF_GetFontKerningSize(font: *const TTF_Font, prev_index: c_int, 99 | index: c_int) -> c_int; 100 | } 101 | -------------------------------------------------------------------------------- /src/sdl2_ttf/font.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CString, CStr}; 2 | use std::os::raw::{c_int, c_long}; 3 | use std::path::Path; 4 | use std::error; 5 | use std::error::Error; 6 | use std::ffi::NulError; 7 | use std::fmt; 8 | use sdl2::surface::Surface; 9 | use sdl2_sys::surface::SDL_Surface; 10 | use sdl2::get_error; 11 | use sdl2::pixels; 12 | use sdl2::pixels::Color; 13 | use sdl2_sys::pixels::SDL_Color; 14 | use sdl2::rwops::RWops; 15 | use ffi; 16 | 17 | /// Converts a rust-SDL2 color to its C ffi representation. 18 | #[inline] 19 | fn color_to_c_color(color: Color) -> SDL_Color { 20 | match color { 21 | pixels::Color::RGB(r, g, b) => SDL_Color { r: r, g: g, b: b, a: 255 }, 22 | pixels::Color::RGBA(r, g, b, a) => SDL_Color { r: r, g: g, b: b, a: a } 23 | } 24 | } 25 | 26 | 27 | // Absolute paths are a workaround for https://github.com/rust-lang-nursery/bitflags/issues/39 . 28 | bitflags! { 29 | /// The styling of a font. 30 | pub flags FontStyle: ::std::os::raw::c_int { 31 | const STYLE_NORMAL = ::ffi::TTF_STYLE_NORMAL, 32 | const STYLE_BOLD = ::ffi::TTF_STYLE_BOLD, 33 | const STYLE_ITALIC = ::ffi::TTF_STYLE_ITALIC, 34 | const STYLE_UNDERLINE = ::ffi::TTF_STYLE_UNDERLINE, 35 | const STYLE_STRIKETHROUGH = ::ffi::TTF_STYLE_STRIKETHROUGH, 36 | } 37 | } 38 | 39 | /// Information about the hinting of a font. 40 | /// See [wikipedia](https://en.wikipedia.org/wiki/Font_hinting) 41 | #[derive(Debug, PartialEq, Clone)] 42 | pub enum Hinting { 43 | Normal = ffi::TTF_HINTING_NORMAL as isize, 44 | Light = ffi::TTF_HINTING_LIGHT as isize, 45 | Mono = ffi::TTF_HINTING_MONO as isize, 46 | None = ffi::TTF_HINTING_NONE as isize 47 | } 48 | 49 | /// Information about a specific glyph (character) in a font face. 50 | #[derive(Debug, PartialEq, Clone)] 51 | pub struct GlyphMetrics { 52 | pub minx: i32, 53 | pub maxx: i32, 54 | pub miny: i32, 55 | pub maxy: i32, 56 | pub advance: i32 57 | } 58 | 59 | /// The result of an `SDL2_TTF` font operation. 60 | pub type FontResult = Result; 61 | 62 | /// A font-related error. 63 | #[derive(Debug)] 64 | pub enum FontError { 65 | /// A Latin-1 encoded byte string is invalid. 66 | InvalidLatin1Text(NulError), 67 | /// A SDL2-related error occured. 68 | SdlError(String), 69 | } 70 | 71 | impl error::Error for FontError { 72 | fn description(&self) -> &str { 73 | match *self { 74 | FontError::InvalidLatin1Text(ref error) => { 75 | error.description() 76 | }, 77 | FontError::SdlError(ref message) => { 78 | message 79 | }, 80 | } 81 | } 82 | 83 | fn cause(&self) -> Option<&error::Error> { 84 | match *self { 85 | FontError::InvalidLatin1Text(ref error) => { 86 | Some(error) 87 | }, 88 | FontError::SdlError(_) => { 89 | None 90 | }, 91 | } 92 | } 93 | } 94 | 95 | impl fmt::Display for FontError { 96 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 97 | match *self { 98 | FontError::InvalidLatin1Text(ref err) => { 99 | write!(f, "Invalid Latin-1 bytes: {}", err.description()) 100 | }, 101 | FontError::SdlError(ref msg) => { 102 | write!(f, "SDL2 error: {}", msg) 103 | }, 104 | } 105 | 106 | } 107 | } 108 | 109 | /// A renderable piece of text in the UTF8 or Latin-1 format. 110 | enum RenderableText<'a> { 111 | Utf8(&'a str), 112 | Latin1(&'a [u8]), 113 | Char(String), 114 | } 115 | impl<'a> RenderableText<'a> { 116 | /// Converts the given text to a c-style string if possible. 117 | fn convert(&self) -> FontResult { 118 | match *self { 119 | RenderableText::Utf8(text) => { 120 | Ok(CString::new(text).unwrap()) 121 | }, 122 | RenderableText::Latin1(bytes) => { 123 | match CString::new(bytes) { 124 | Err(err) => { 125 | Err(FontError::InvalidLatin1Text(err)) 126 | }, 127 | Ok(cstring) => { 128 | Ok(cstring) 129 | } 130 | } 131 | }, 132 | RenderableText::Char(ref string) => { 133 | Ok(CString::new(string.as_bytes()).unwrap()) 134 | } 135 | } 136 | } 137 | } 138 | 139 | /// A builder for a font rendering. 140 | #[must_use] 141 | pub struct PartialRendering<'a> { 142 | text: RenderableText<'a>, 143 | font: &'a Font<'a>, 144 | } 145 | 146 | /// Converts the given raw pointer to a surface. 147 | fn convert_to_surface<'a>(raw: *mut SDL_Surface) -> FontResult> { 148 | if (raw as *mut ()).is_null() { 149 | Err(FontError::SdlError(get_error())) 150 | } else { 151 | Ok(unsafe { 152 | Surface::from_ll(raw) 153 | }) 154 | } 155 | } 156 | 157 | impl<'a> PartialRendering<'a> { 158 | /// Renders the text in *solid* mode. 159 | /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42) 160 | /// for an explanation. 161 | pub fn solid<'b, T>(self, color: T ) 162 | -> FontResult> where T: Into { 163 | let source = try!(self.text.convert()); 164 | let color = color_to_c_color(color.into()); 165 | let raw = unsafe { 166 | match self.text { 167 | RenderableText::Utf8(_) | RenderableText::Char(_) => { 168 | ffi::TTF_RenderUTF8_Solid(self.font.raw(), 169 | source.as_ptr(), color) 170 | }, 171 | RenderableText::Latin1(_) => { 172 | ffi::TTF_RenderText_Solid(self.font.raw(), 173 | source.as_ptr(), color) 174 | }, 175 | } 176 | }; 177 | convert_to_surface(raw) 178 | } 179 | 180 | /// Renders the text in *shaded* mode. 181 | /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42) 182 | /// for an explanation. 183 | pub fn shaded<'b, T>(self, color: T, background: T) 184 | -> FontResult> where T: Into { 185 | let source = try!(self.text.convert()); 186 | let foreground = color_to_c_color(color.into()); 187 | let background = color_to_c_color(background.into()); 188 | let raw = unsafe { 189 | match self.text { 190 | RenderableText::Utf8(_) | RenderableText::Char(_) => { 191 | ffi::TTF_RenderUTF8_Shaded(self.font.raw(), 192 | source.as_ptr(), foreground, background) 193 | }, 194 | RenderableText::Latin1(_) => { 195 | ffi::TTF_RenderText_Shaded(self.font.raw(), 196 | source.as_ptr(), foreground, background) 197 | }, 198 | } 199 | }; 200 | convert_to_surface(raw) 201 | } 202 | 203 | /// Renders the text in *blended* mode. 204 | /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42) 205 | /// for an explanation. 206 | pub fn blended<'b, T>(self, color: T) 207 | -> FontResult> where T: Into { 208 | let source = try!(self.text.convert()); 209 | let color = color_to_c_color(color.into()); 210 | let raw = unsafe { 211 | match self.text { 212 | RenderableText::Utf8(_) | RenderableText::Char(_) => { 213 | ffi::TTF_RenderUTF8_Blended(self.font.raw(), 214 | source.as_ptr(), color) 215 | }, 216 | RenderableText::Latin1(_) => { 217 | ffi::TTF_RenderText_Blended(self.font.raw(), 218 | source.as_ptr(), color) 219 | }, 220 | } 221 | }; 222 | convert_to_surface(raw) 223 | } 224 | 225 | /// Renders the text in *blended* mode but wrapping the words if the width 226 | /// exceeds the given maximum width. 227 | /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42) 228 | /// for an explanation of the mode. 229 | pub fn blended_wrapped<'b, T>(self, color: T, wrap_max_width: u32) 230 | -> FontResult> where T: Into { 231 | let source = try!(self.text.convert()); 232 | let color = color_to_c_color(color.into()); 233 | let raw = unsafe { 234 | match self.text { 235 | RenderableText::Utf8(_) | RenderableText::Char(_) => { 236 | ffi::TTF_RenderUTF8_Blended_Wrapped(self.font.raw(), 237 | source.as_ptr(), color, wrap_max_width) 238 | }, 239 | RenderableText::Latin1(_) => { 240 | ffi::TTF_RenderText_Blended_Wrapped(self.font.raw(), 241 | source.as_ptr(), color, wrap_max_width) 242 | }, 243 | } 244 | }; 245 | convert_to_surface(raw) 246 | } 247 | } 248 | 249 | /// A loaded TTF font. 250 | pub struct Font<'a> { 251 | raw: *const ffi::TTF_Font, 252 | // RWops is only stored here because it must not outlive 253 | // the Font struct, and this RWops should not be used by 254 | // anything else 255 | // None means that the RWops is handled by SDL itself, 256 | // and Some(rwops) means that the RWops is handled by the Rust 257 | // side 258 | #[allow(dead_code)] 259 | rwops:Option> 260 | } 261 | 262 | impl<'a> Drop for Font<'a> { 263 | fn drop(&mut self) { 264 | unsafe { 265 | // avoid close font after quit() 266 | if ffi::TTF_WasInit() == 1 { 267 | ffi::TTF_CloseFont(self.raw); 268 | } 269 | } 270 | } 271 | } 272 | 273 | /// Internally used to load a font (for internal visibility). 274 | pub fn internal_load_font(path: &Path, ptsize: u16) -> Result { 275 | unsafe { 276 | let cstring = CString::new(path.to_str().unwrap()).unwrap(); 277 | let raw = ffi::TTF_OpenFont(cstring.as_ptr(), ptsize as c_int); 278 | if raw.is_null() { 279 | Err(get_error()) 280 | } else { 281 | Ok(Font { raw: raw, rwops: None }) 282 | } 283 | } 284 | } 285 | 286 | /// Internally used to load a font (for internal visibility). 287 | pub fn internal_load_font_from_ll<'a>(raw: *const ffi::TTF_Font, rwops: Option>) 288 | -> Font<'a> { 289 | Font { raw: raw, rwops: rwops } 290 | } 291 | 292 | /// Internally used to load a font (for internal visibility). 293 | pub fn internal_load_font_at_index(path: &Path, index: u32, ptsize: u16) 294 | -> Result { 295 | unsafe { 296 | let cstring = CString::new(path.to_str().unwrap().as_bytes()) 297 | .unwrap(); 298 | let raw = ffi::TTF_OpenFontIndex(cstring.as_ptr(), 299 | ptsize as c_int, index as c_long); 300 | if raw.is_null() { 301 | Err(get_error()) 302 | } else { 303 | Ok(Font { raw: raw, rwops: None }) 304 | } 305 | } 306 | } 307 | 308 | impl<'a> Font<'a> { 309 | /// Returns the underlying C font object. 310 | unsafe fn raw(&self) -> *const ffi::TTF_Font { 311 | self.raw 312 | } 313 | 314 | /// Starts specifying a rendering of the given UTF-8-encoded text. 315 | pub fn render(&'a self, text: &'a str) -> PartialRendering<'a> { 316 | PartialRendering { 317 | text: RenderableText::Utf8(text), 318 | font: self, 319 | } 320 | } 321 | 322 | /// Starts specifying a rendering of the given Latin-1-encoded text. 323 | pub fn render_latin1(&'a self, text: &'a [u8]) -> PartialRendering<'a> { 324 | PartialRendering { 325 | text: RenderableText::Latin1(text), 326 | font: self, 327 | } 328 | } 329 | 330 | /// Starts specifying a rendering of the given UTF-8-encoded character. 331 | pub fn render_char(&'a self, ch: char) -> PartialRendering<'a> { 332 | let mut s = String::new(); 333 | s.push(ch); 334 | PartialRendering { 335 | text: RenderableText::Char(s), 336 | font: self, 337 | } 338 | } 339 | 340 | /// Returns the width and height of the given text when rendered using this 341 | /// font. 342 | #[allow(unused_mut)] 343 | pub fn size_of(&self, text: &str) -> FontResult<(u32, u32)> { 344 | let c_string = try!(RenderableText::Utf8(text).convert()); 345 | let (res, size) = unsafe { 346 | let mut w = 0; // mutated by C code 347 | let mut h = 0; // mutated by C code 348 | let ret = ffi::TTF_SizeUTF8(self.raw, c_string.as_ptr(), &w, &h); 349 | (ret, (w as u32, h as u32)) 350 | }; 351 | if res == 0 { 352 | Ok(size) 353 | } else { 354 | Err(FontError::SdlError(get_error())) 355 | } 356 | } 357 | 358 | /// Returns the width and height of the given text when rendered using this 359 | /// font. 360 | #[allow(unused_mut)] 361 | pub fn size_of_latin1(&self, text: &[u8]) 362 | -> FontResult<(u32, u32)> { 363 | let c_string = try!(RenderableText::Latin1(text).convert()); 364 | let (res, size) = unsafe { 365 | let mut w = 0; // mutated by C code 366 | let mut h = 0; // mutated by C code 367 | let ret = ffi::TTF_SizeText(self.raw, c_string.as_ptr(), &w, &h); 368 | (ret, (w as u32, h as u32)) 369 | }; 370 | if res == 0 { 371 | Ok(size) 372 | } else { 373 | Err(FontError::SdlError(get_error())) 374 | } 375 | } 376 | 377 | /// Returns the width and height of the given text when rendered using this 378 | /// font. 379 | pub fn size_of_char(&self, ch: char) -> FontResult<(u32, u32)> { 380 | let mut s = String::new(); 381 | s.push(ch); 382 | self.size_of(&s) 383 | } 384 | 385 | /// Returns the font's style flags. 386 | pub fn get_style(&self) -> FontStyle { 387 | unsafe { 388 | let raw = ffi::TTF_GetFontStyle(self.raw); 389 | FontStyle::from_bits_truncate(raw) 390 | } 391 | } 392 | 393 | /// Sets the font's style flags. 394 | pub fn set_style(&mut self, styles: FontStyle) { 395 | unsafe { 396 | ffi::TTF_SetFontStyle(self.raw, styles.bits()) 397 | } 398 | } 399 | 400 | /// Returns the width of the font's outline. 401 | pub fn get_outline_width(&self) -> u16 { 402 | unsafe { 403 | ffi::TTF_GetFontOutline(self.raw) as u16 404 | } 405 | } 406 | 407 | /// Sets the width of the font's outline. 408 | pub fn set_outline_width(&mut self, width: u16) { 409 | unsafe { 410 | ffi::TTF_SetFontOutline(self.raw, width as c_int) 411 | } 412 | } 413 | 414 | /// Returns the font's freetype hints. 415 | pub fn get_hinting(&self) -> Hinting { 416 | unsafe { 417 | match ffi::TTF_GetFontHinting(self.raw) as c_int { 418 | ffi::TTF_HINTING_NORMAL => Hinting::Normal, 419 | ffi::TTF_HINTING_LIGHT => Hinting::Light, 420 | ffi::TTF_HINTING_MONO => Hinting::Mono, 421 | ffi::TTF_HINTING_NONE | _ => Hinting::None 422 | } 423 | } 424 | } 425 | 426 | /// Sets the font's freetype hints. 427 | pub fn set_hinting(&mut self, hinting: Hinting) { 428 | unsafe { 429 | ffi::TTF_SetFontHinting(self.raw, hinting as c_int) 430 | } 431 | } 432 | 433 | /// Returns whether the font is kerning. 434 | pub fn get_kerning(&self) -> bool { 435 | unsafe { 436 | ffi::TTF_GetFontKerning(self.raw) != 0 437 | } 438 | } 439 | 440 | /// Sets whether the font should use kerning. 441 | pub fn set_kerning(&mut self, kerning: bool) { 442 | unsafe { 443 | ffi::TTF_SetFontKerning(self.raw, kerning as c_int) 444 | } 445 | } 446 | 447 | pub fn height(&self) -> i32 { 448 | //! Get font maximum total height. 449 | unsafe { 450 | ffi::TTF_FontHeight(self.raw) as i32 451 | } 452 | } 453 | 454 | /// Returns the font's highest ascent (height above base). 455 | pub fn ascent(&self) -> i32 { 456 | unsafe { 457 | ffi::TTF_FontAscent(self.raw) as i32 458 | } 459 | } 460 | 461 | /// Returns the font's lowest descent (height below base). 462 | /// This is a negative number. 463 | pub fn descent(&self) -> i32 { 464 | unsafe { 465 | ffi::TTF_FontDescent(self.raw) as i32 466 | } 467 | } 468 | 469 | /// Returns the recommended line spacing for text rendered with this font. 470 | pub fn recommended_line_spacing(&self) -> i32 { 471 | unsafe { 472 | ffi::TTF_FontLineSkip(self.raw) as i32 473 | } 474 | } 475 | 476 | /// Returns the number of faces in this font. 477 | pub fn face_count(&self) -> u16 { 478 | unsafe { 479 | ffi::TTF_FontFaces(self.raw) as u16 480 | } 481 | } 482 | 483 | /// Returns whether the font is monospaced. 484 | pub fn face_is_fixed_width(&self) -> bool { 485 | unsafe { 486 | ffi::TTF_FontFaceIsFixedWidth(self.raw) != 0 487 | } 488 | } 489 | 490 | /// Returns the family name of the current font face. 491 | pub fn face_family_name(&self) -> Option { 492 | unsafe { 493 | // not owns buffer 494 | let cname = ffi::TTF_FontFaceFamilyName(self.raw); 495 | if cname.is_null() { 496 | None 497 | } else { 498 | Some(String::from_utf8_lossy(CStr::from_ptr(cname).to_bytes()).to_string()) 499 | } 500 | } 501 | } 502 | 503 | /// Returns the name of the current font face. 504 | pub fn face_style_name(&self) -> Option { 505 | unsafe { 506 | let cname = ffi::TTF_FontFaceStyleName(self.raw); 507 | if cname.is_null() { 508 | None 509 | } else { 510 | Some(String::from_utf8_lossy(CStr::from_ptr(cname).to_bytes()).to_string()) 511 | } 512 | } 513 | } 514 | 515 | /// Returns the index of the given character in this font face. 516 | pub fn find_glyph(&self, ch: char) -> Option { 517 | unsafe { 518 | let ret = ffi::TTF_GlyphIsProvided(self.raw, ch as u16); 519 | if ret == 0 { 520 | None 521 | } else { 522 | Some(ret as u16) 523 | } 524 | } 525 | } 526 | 527 | /// Returns the glyph metrics of the given character in this font face. 528 | pub fn find_glyph_metrics(&self, ch: char) -> Option { 529 | let minx = 0; 530 | let maxx = 0; 531 | let miny = 0; 532 | let maxy = 0; 533 | let advance = 0; 534 | let ret = unsafe { 535 | ffi::TTF_GlyphMetrics( 536 | self.raw, ch as u16, &minx, &maxx, &miny, &maxy, &advance 537 | ) 538 | }; 539 | if ret == 0 { 540 | Some(GlyphMetrics { 541 | minx: minx as i32, maxx: maxx as i32, miny: miny as i32, 542 | maxy: maxy as i32, advance: advance as i32 543 | } ) 544 | } else { 545 | None 546 | } 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Rust-Empty: An Makefile to get started with Rust 2 | # https://github.com/bvssvni/rust-empty 3 | # 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2014 Sven Nilsen 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | # this software and associated documentation files (the "Software"), to deal in 10 | # the Software without restriction, including without limitation the rights to 11 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | # the Software, and to permit persons to whom the Software is furnished to do so, 13 | # subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | SHELL := /bin/bash 26 | 27 | # The default make command. 28 | # Change this to 'lib' if you are building a library. 29 | DEFAULT = lib 30 | # The entry file of library source. 31 | # Change this to support multi-crate source structure. 32 | # For advanced usage, you can rename the file 'rust-empty.mk' 33 | # and call it with 'make -f rust-empty.mk ' from your Makefile. 34 | LIB_ENTRY_FILE = src/sdl2_ttf/lib.rs 35 | # The entry file of executable source. 36 | EXE_ENTRY_FILE = src/demo/main.rs 37 | 38 | EXAMPLE_FILES = examples/*.rs 39 | SOURCE_FILES = $(shell test -e src/ && find src -type f) 40 | 41 | COMPILER = rustc 42 | 43 | # For release: 44 | COMPILER_FLAGS = -O 45 | # For debugging: 46 | # COMPILER_FLAGS = -g 47 | 48 | UNAME=$(shell uname) 49 | 50 | ifeq ($(UNAME),Darwin) 51 | # If the user wasn't explicit, see if SDL2 library exists 52 | ifeq ("$(strip $(SDL_MODE))","") 53 | SDL_CHECK=$(shell pkg-config --exists sdl2) 54 | ifeq ($(SDL_CHECK),0) 55 | SDL_MODE = dylib 56 | else 57 | SDL_MODE = framework 58 | endif 59 | endif 60 | 61 | ifeq ($(SDL_MODE),framework) 62 | COMPILER_FLAGS+=--cfg mac_framework 63 | else 64 | COMPILER_FLAGS+=--cfg mac_dylib 65 | endif 66 | endif 67 | 68 | RUSTDOC = rustdoc 69 | 70 | # Extracts target from rustc. 71 | TARGET = $(shell rustc --version verbose 2> /dev/null | awk "/host:/ { print \$$2 }") 72 | # TARGET = x86_64-unknown-linux-gnu 73 | # TARGET = x86_64-apple-darwin 74 | 75 | TARGET_LIB_DIR = target/deps/ 76 | 77 | # Ask 'rustc' the file name of the library and use a dummy name if the source has not been created yet. 78 | # The dummy file name is used to trigger the creation of the source first time. 79 | # Next time 'rustc' will return the right file name. 80 | RLIB_FILE = $(shell (rustc --crate-type=rlib --crate-file-name "$(LIB_ENTRY_FILE)" 2> /dev/null) || (echo "dummy.rlib")) 81 | # You can't have quotes around paths because 'make' doesn't see it exists. 82 | RLIB = target/$(RLIB_FILE) 83 | DYLIB_FILE = $(shell (rustc --crate-type=dylib --crate-file-name "$(LIB_ENTRY_FILE)" 2> /dev/null) || (echo "dummy.dylib")) 84 | DYLIB = target/$(DYLIB_FILE) 85 | 86 | # Use 'VERBOSE=1' to echo all commands, for example 'make help VERBOSE=1'. 87 | ifdef VERBOSE 88 | Q := 89 | else 90 | Q := @ 91 | endif 92 | 93 | all: $(DEFAULT) 94 | 95 | help: 96 | $(Q)echo "--- rust-empty (0.6 003)" 97 | $(Q)echo "make run - Runs executable" 98 | $(Q)echo "make exe - Builds main executable" 99 | $(Q)echo "make lib - Both static and dynamic library" 100 | $(Q)echo "make rlib - Static library" 101 | $(Q)echo "make dylib - Dynamic library" 102 | $(Q)echo "make test - Tests library internally and externally" 103 | $(Q)echo "make test-internal - Tests library internally" 104 | $(Q)echo "make test-external - Tests library externally" 105 | $(Q)echo "make bench - Benchmarks library internally and externally" 106 | $(Q)echo "make bench-internal - Benchmarks library internally" 107 | $(Q)echo "make bench-external - Benchmarks library externally" 108 | $(Q)echo "make doc - Builds documentation for library" 109 | $(Q)echo "make git-ignore - Setup files to be ignored by Git" 110 | $(Q)echo "make examples - Builds examples" 111 | $(Q)echo "make cargo-lite-exe - Setup executable package" 112 | $(Q)echo "make cargo-lite-lib - Setup library package" 113 | $(Q)echo "make cargo-exe - Setup executable package" 114 | $(Q)echo "make cargo-lib - Setup library package" 115 | $(Q)echo "make rust-ci-lib - Setup Travis CI Rust library" 116 | $(Q)echo "make rust-ci-exe - Setup Travis CI Rust executable" 117 | $(Q)echo "make rusti - Setup 'rusti.sh' for interactive Rust" 118 | $(Q)echo "make watch - Setup 'watch.sh' for compilation on save" 119 | $(Q)echo "make loc - Count lines of code in src folder" 120 | $(Q)echo "make nightly-install - Installs Rust nightly build" 121 | $(Q)echo "make nightly-uninstall - Uninstalls Rust nightly build" 122 | $(Q)echo "make clean - Deletes binaries and documentation." 123 | $(Q)echo "make clear-project - WARNING: Deletes project files except 'Makefile'" 124 | $(Q)echo "make clear-git - WARNING: Deletes Git setup" 125 | $(Q)echo "make symlink-build - Creates a script for building dependencies" 126 | $(Q)echo "make symlink-info - Symlinked libraries dependency info" 127 | $(Q)echo "make target-dir - Creates directory for current target" 128 | 129 | .PHONY: \ 130 | bench \ 131 | bench-internal \ 132 | bench-external \ 133 | cargo-lib \ 134 | cargo-exe \ 135 | cargo-lite-lib \ 136 | cargo-lite-exe \ 137 | clean \ 138 | clear-git \ 139 | clear-project \ 140 | loc \ 141 | nightly-install \ 142 | nightly-uninstall \ 143 | run \ 144 | rusti \ 145 | rust-ci-lib \ 146 | rust-ci-exe \ 147 | symlink-build \ 148 | symlink-info \ 149 | target-dir \ 150 | test \ 151 | test-internal \ 152 | test-external \ 153 | watch 154 | 155 | nightly-install: 156 | $(Q)cd ~ \ 157 | && curl -s http://www.rust-lang.org/rustup.sh > rustup.sh \ 158 | && ( \ 159 | echo "Rust install-script stored as '~/rustup.sh'" ; \ 160 | read -p "Do you want to install? [y/n]:" -n 1 -r ; \ 161 | echo "" ; \ 162 | if [[ $$REPLY =~ ^[Yy]$$ ]] ; \ 163 | then \ 164 | cat rustup.sh | sudo sh ; \ 165 | fi \ 166 | ) 167 | 168 | nightly-uninstall: 169 | $(Q)cd ~ \ 170 | && curl -s http://www.rust-lang.org/rustup.sh > rustup.sh \ 171 | && ( \ 172 | echo "Rust install-script stored as '~/rustup.sh'" ; \ 173 | read -p "Do you want to uninstall? [y/n]:" -n 1 -r ; \ 174 | echo "" ; \ 175 | if [[ $$REPLY =~ ^[Yy]$$ ]] ; \ 176 | then \ 177 | cat rustup.sh | sudo sh -s -- --uninstall ; \ 178 | fi \ 179 | ) 180 | 181 | cargo-lite-exe: $(EXE_ENTRY_FILE) 182 | $(Q)( \ 183 | test -e cargo-lite.conf \ 184 | && echo "--- The file 'cargo-lite.conf' already exists" \ 185 | ) \ 186 | || \ 187 | ( \ 188 | echo -e "deps = [\n]\n\n[build]\ncrate_root = \"$(EXE_ENTRY_FILE)\"\nrustc_args = []\n" > cargo-lite.conf \ 189 | && echo "--- Created 'cargo-lite.conf' for executable" \ 190 | && cat cargo-lite.conf \ 191 | ) 192 | 193 | cargo-lite-lib: $(LIB_ENTRY_FILE) 194 | $(Q)( \ 195 | test -e cargo-lite.conf \ 196 | && echo "--- The file 'cargo-lite.conf' already exists" \ 197 | ) \ 198 | || \ 199 | ( \ 200 | echo -e "deps = [\n]\n\n[build]\ncrate_root = \"$(LIB_ENTRY_FILE)\"\ncrate_type = \"library\"\nrustc_args = []\n" > cargo-lite.conf \ 201 | && echo "--- Created 'cargo-lite.conf' for library" \ 202 | && cat cargo-lite.conf \ 203 | ) 204 | 205 | cargo-exe: $(EXE_ENTRY_FILE) 206 | $(Q)( \ 207 | test -e Cargo.toml \ 208 | && echo "--- The file 'Cargo.toml' already exists" \ 209 | ) \ 210 | || \ 211 | ( \ 212 | name=$${PWD##/*/} ; \ 213 | readme=$$((test -e README.md && echo -e "readme = \"README.md\"") || ("")) ; \ 214 | echo -e "[package]\n\nname = \"$$name\"\nversion = \"0.0.0\"\n$$readme\nauthors = [\"Your Name \"]\ntags = []\n\n[[bin]]\n\nname = \"$$name\"\npath = \"$(EXE_ENTRY_FILE)\"\n" > Cargo.toml \ 215 | && echo "--- Created 'Cargo.toml' for executable" \ 216 | && cat Cargo.toml \ 217 | ) 218 | 219 | cargo-lib: $(LIB_ENTRY_FILE) 220 | $(Q)( \ 221 | test -e Cargo.toml \ 222 | && echo "--- The file 'Cargo.toml' already exists" \ 223 | ) \ 224 | || \ 225 | ( \ 226 | name=$${PWD##/*/} ; \ 227 | readme=$$((test -e README.md && echo -e "readme = \"README.md\"") || ("")) ; \ 228 | echo -e "[package]\n\nname = \"$$name\"\nversion = \"0.0.0\"\n$$readme\nauthors = [\"Your Name \"]\ntags = []\n\n[[lib]]\n\nname = \"$$name\"\npath = \"$(LIB_ENTRY_FILE)\"\n" > Cargo.toml \ 229 | && echo "--- Created 'Cargo.toml' for executable" \ 230 | && cat Cargo.toml \ 231 | ) 232 | 233 | rust-ci-lib: $(LIB_ENTRY_FILE) 234 | $(Q)( \ 235 | test -e .travis.yml \ 236 | && echo "--- The file '.travis.yml' already exists" \ 237 | ) \ 238 | || \ 239 | ( \ 240 | echo -e "install:\n - wget http://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz -O - | sudo tar zxf - --strip-components 1 -C /usr/local\nscript:\n - make lib\n" > .travis.yml \ 241 | && echo "--- Created '.travis.yml' for library" \ 242 | && cat .travis.yml \ 243 | ) 244 | 245 | rust-ci-exe: $(EXE_ENTRY_FILE) 246 | $(Q)( \ 247 | test -e .travis.yml \ 248 | && echo "--- The file '.travis.yml' already exists" \ 249 | ) \ 250 | || \ 251 | ( \ 252 | echo -e "install:\n - wget http://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz -O - | sudo tar zxf - --strip-components 1 -C /usr/local\nscript:\n - make exe\n" > .travis.yml \ 253 | && echo "--- Created '.travis.yml' for executable" \ 254 | && cat .travis.yml \ 255 | ) 256 | 257 | doc: $(SOURCE_FILES) | src/ 258 | $(Q)$(RUSTDOC) $(LIB_ENTRY_FILE) -L "$(TARGET_LIB_DIR)" \ 259 | && echo "--- Built documentation" 260 | 261 | run: exe 262 | $(Q)cd bin/ \ 263 | && ./main 264 | 265 | target-dir: $(TARGET_LIB_DIR) 266 | 267 | exe: bin/main | $(TARGET_LIB_DIR) 268 | 269 | bin/main: $(SOURCE_FILES) | bin/ $(EXE_ENTRY_FILE) 270 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) $(EXE_ENTRY_FILE) -o bin/main -L "$(TARGET_LIB_DIR)" -L "target" \ 271 | && echo "--- Built executable" \ 272 | && echo "--- Type 'make run' to run executable" 273 | 274 | test: test-internal test-external 275 | $(Q)echo "--- Internal tests succeeded" \ 276 | && echo "--- External tests succeeded" 277 | 278 | test-external: bin/test-external 279 | $(Q)cd "bin/" \ 280 | && ./test-external 281 | 282 | bin/test-external: $(SOURCE_FILES) | rlib bin/ src/test.rs 283 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) --test src/test.rs -o bin/test-external -L "$(TARGET_LIB_DIR)" -L "target" \ 284 | && echo "--- Built external test runner" 285 | 286 | test-internal: bin/test-internal 287 | $(Q)cd "bin/" \ 288 | && ./test-internal 289 | 290 | bin/test-internal: $(SOURCE_FILES) | rlib src/ bin/ 291 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) --test $(LIB_ENTRY_FILE) -o bin/test-internal -L "$(TARGET_LIB_DIR)" -L "target" \ 292 | && echo "--- Built internal test runner" 293 | 294 | bench: bench-internal bench-external 295 | 296 | bench-external: test-external 297 | $(Q)bin/test-external --bench 298 | 299 | bench-internal: test-internal 300 | $(Q)bin/test-internal --bench 301 | 302 | lib: rlib dylib 303 | $(Q)echo "--- Type 'make test' to test library" 304 | 305 | rlib: $(RLIB) 306 | 307 | $(RLIB): $(SOURCE_FILES) | $(LIB_ENTRY_FILE) $(TARGET_LIB_DIR) 308 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) --crate-type=rlib $(LIB_ENTRY_FILE) -L "$(TARGET_LIB_DIR)" --out-dir "target" \ 309 | && echo "--- Built rlib" 310 | 311 | dylib: $(DYLIB) 312 | 313 | $(DYLIB): $(SOURCE_FILES) | $(LIB_ENTRY_FILE) $(TARGET_LIB_DIR) 314 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) --crate-type=dylib $(LIB_ENTRY_FILE) -L "$(TARGET_LIB_DIR)" --out-dir "target/" \ 315 | && echo "--- Built dylib" 316 | 317 | bin/: 318 | $(Q)mkdir -p bin 319 | 320 | $(TARGET_LIB_DIR): 321 | $(Q)mkdir -p $(TARGET_LIB_DIR) 322 | 323 | src/: 324 | $(Q)mkdir -p src 325 | 326 | examples-dir: 327 | $(Q)test -e examples \ 328 | || \ 329 | ( \ 330 | mkdir examples \ 331 | && echo -e "fn main() {\n\tprintln!(\"Hello!\");\n}\n" > examples/hello.rs \ 332 | && echo "--- Created examples folder" \ 333 | ) 334 | 335 | rust-dir: 336 | $(Q)mkdir -p .rust 337 | 338 | git-ignore: 339 | $(Q)( \ 340 | test -e .gitignore \ 341 | && echo "--- The file '.gitignore' already exists" \ 342 | ) \ 343 | || \ 344 | ( \ 345 | echo -e ".DS_Store\n*~\n*#\n*.o\n*.so\n*.swp\n*.dylib\n*.dSYM\n*.dll\n*.rlib\n*.dummy\n*.exe\n*-test\n/bin/main\n/bin/test-internal\n/bin/test-external\n/doc/\n/target/\n/build/\n/.rust/\nrusti.sh\nwatch.sh\n/examples/**\n!/examples/*.rs\n!/examples/assets/" > .gitignore \ 346 | && echo "--- Created '.gitignore' for git" \ 347 | && cat .gitignore \ 348 | ) 349 | 350 | examples: $(EXAMPLE_FILES) 351 | 352 | $(EXAMPLE_FILES): lib examples-dir 353 | $(Q)$(COMPILER) --target "$(TARGET)" $(COMPILER_FLAGS) $@ -L "$(TARGET_LIB_DIR)" -L "target" --out-dir examples/ \ 354 | && echo "--- Built '$@' (make $@)" 355 | 356 | $(EXE_ENTRY_FILE): | src/ 357 | $(Q)test -e $(EXE_ENTRY_FILE) \ 358 | || \ 359 | ( \ 360 | echo -e "fn main() {\n\tprintln!(\"Hello world!\");\n}" > $(EXE_ENTRY_FILE) \ 361 | ) 362 | 363 | src/test.rs: | src/ 364 | $(Q)test -e src/test.rs \ 365 | || \ 366 | ( \ 367 | touch src/test.rs \ 368 | ) 369 | 370 | $(LIB_ENTRY_FILE): | src/ 371 | $(Q)test -e $(LIB_ENTRY_FILE) \ 372 | || \ 373 | ( \ 374 | echo -e "#![crate_id = \"\"]\n#![deny(missing_doc)]\n\n//! Documentation goes here.\n" > $(LIB_ENTRY_FILE) \ 375 | ) 376 | 377 | clean: 378 | $(Q)rm -f "$(RLIB)" 379 | $(Q)rm -f "$(DYLIB)" 380 | $(Q)rm -rf "doc/" 381 | $(Q)rm -f "bin/main" 382 | $(Q)rm -f "bin/test-internal" 383 | $(Q)rm -f "bin/test-external" 384 | $(Q)echo "--- Deleted binaries and documentation" 385 | 386 | clear-project: 387 | $(Q)rm -f ".symlink-info" 388 | $(Q)rm -f "cargo-lite.conf" 389 | $(Q)rm -f "Cargo.toml" 390 | $(Q)rm -f ".travis.yml" 391 | $(Q)rm -f "rusti.sh" 392 | $(Q)rm -f "watch.sh" 393 | $(Q)rm -rf "target/" 394 | $(Q)rm -rf "src/" 395 | $(Q)rm -rf "bin/" 396 | $(Q)rm -rf "examples/" 397 | $(Q)rm -rf "doc/" 398 | $(Q)echo "--- Removed all source files, binaries and documentation" \ 399 | && echo "--- Content in project folder" \ 400 | && ls -a 401 | 402 | clear-git: 403 | $(Q)rm -f ".gitignore" 404 | $(Q)rm -rf ".git" 405 | $(Q)echo "--- Removed Git" \ 406 | && echo "--- Content in project folder" \ 407 | && ls -a 408 | 409 | # borrowed from http://stackoverflow.com/q/649246/1256624 410 | define RUSTI_SCRIPT 411 | #!/bin/bash 412 | 413 | #written by mcpherrin 414 | 415 | while true; do 416 | echo -n "> " 417 | read line 418 | TMP="`mktemp r.XXXXXX`" 419 | $(COMPILER) - -o $$TMP -L "$(TARGET_LIB_DIR)" < rusti.sh \ 468 | && chmod +x rusti.sh \ 469 | && echo "--- Created 'rusti.sh'" \ 470 | && echo "--- Type './rusti.sh' to start interactive Rust" \ 471 | ) 472 | 473 | # borrowed from http://stackoverflow.com/q/649246/1256624 474 | define WATCH_SCRIPT 475 | #!/bin/bash 476 | 477 | #written by zzmp 478 | 479 | # This script will recompile a rust project using `make` 480 | # every time something in the specified directory changes. 481 | 482 | # Watch files in infinite loop 483 | watch () { 484 | UNAME=$$(uname) 485 | if [ -e "$$2" ]; then 486 | echo "Watching files in $$2.." 487 | CTIME=$$(date "+%s") 488 | while :; do 489 | sleep 1 490 | for f in `find $$2 -type f -name "*.rs"`; do 491 | if [[ $$UNAME == "Darwin" ]]; then 492 | st_mtime=$$(stat -f "%m" "$$f") 493 | elif [[ $$UNAME == "FreeBSD" ]]; then 494 | st_mtime=$$(stat -f "%m" "$$f") 495 | else 496 | st_mtime=$$(stat -c "%Y" "$$f") 497 | fi 498 | if [ $$st_mtime -gt $$CTIME ]; then 499 | CTIME=$$(date "+%s") 500 | echo "~~~ Rebuilding" 501 | $$1 502 | if [ ! $$? -eq 0 ]; then 503 | echo "" 504 | fi 505 | fi 506 | done 507 | done 508 | else 509 | echo "$$2 is not a valid directory" 510 | fi 511 | } 512 | 513 | # Capture user input with defaults 514 | CMD=$${1:-make} 515 | DIR=$${2:-src} 516 | 517 | if [ $${CMD:0:2} = '-h' ]; then 518 | echo ' 519 | This script will recompile a rust project using `make` 520 | every time something in the specified directory changes. 521 | 522 | Use: ./watch.sh [CMD] [DIR] 523 | Example: ./watch.sh "make run" src 524 | 525 | CMD: Command to execute 526 | Complex commands may be passed as strings 527 | `make` by default 528 | DIR: Directory to watch 529 | src by default 530 | 531 | If DIR is supplied, CMD must be as well.\n' 532 | else 533 | watch "$$CMD" "$$DIR" 534 | fi 535 | 536 | endef 537 | export WATCH_SCRIPT 538 | 539 | watch: $(TARGET_LIB_DIR) 540 | $(Q)( \ 541 | test -e watch.sh \ 542 | && echo "--- The file 'watch.sh' already exists" \ 543 | ) \ 544 | || \ 545 | ( \ 546 | echo -e "$$WATCH_SCRIPT" > watch.sh \ 547 | && chmod +x watch.sh \ 548 | && echo "--- Created 'watch.sh'" \ 549 | && echo "--- Type './watch.sh' to start compilation on save" \ 550 | && echo "--- Type './watch.sh -h' for more options" \ 551 | ) 552 | 553 | # borrowed from http://stackoverflow.com/q/649246/1256624 554 | define SYMLINK_BUILD_SCRIPT 555 | #!/bin/bash 556 | # written by bvssvni 557 | # Modify the setting to do conditional compilation. 558 | # For example "--cfg my_feature" 559 | SETTINGS="" 560 | # ================================================ 561 | 562 | MAKE=make 563 | if [ "$$OS" == "Windows_NT" ]; then 564 | MAKE=mingw32-make 565 | fi 566 | 567 | # Checks if an item exists in an array. 568 | # Copied from http://stackoverflow.com/questions/3685970/check-if-an-array-contains-a-value 569 | function contains() { 570 | local n=$$# 571 | local value=$${!n} 572 | for ((i=1;i < $$#;i++)) { 573 | if [ "$${!i}" == "$${value}" ]; then 574 | echo "y" 575 | return 0 576 | fi 577 | } 578 | echo "n" 579 | return 1 580 | } 581 | 582 | # This is a counter used to insert dependencies. 583 | # It is global because we need an array of all the 584 | # visited dependencies. 585 | i=0 586 | function build_deps { 587 | local current=$$(pwd) 588 | for symlib in $$(find $(TARGET_LIB_DIR) -type l) ; do 589 | cd $$current 590 | echo $$symlib 591 | local original_file=$$(readlink $$symlib) 592 | local original_dir=$$(dirname $$original_file) 593 | cd $$original_dir 594 | 595 | # Go to the git root directory. 596 | local current_git_dir=$$(git rev-parse --show-toplevel) 597 | echo "--- Parent $$current" 598 | echo "--- Child $$current_git_dir" 599 | cd $$current_git_dir 600 | 601 | # Skip building if it is already built. 602 | if [ $$(contains "$${git_dir[@]}" $$current_git_dir) == "y" ]; then 603 | echo "--- Visited $$current_git_dir" 604 | continue 605 | fi 606 | 607 | # Remember git directory to not build it twice 608 | git_dir[i]=$$current_git_dir 609 | let i+=1 610 | 611 | # Visit the symlinks and build the dependencies 612 | build_deps 613 | 614 | # First check for a 'build.sh' script with default settings. 615 | # Check for additional 'rust-empty.mk' file. \ 616 | # Compile with the settings flags. \ 617 | # If no other options, build with make. 618 | ( \ 619 | test -e build.sh \ 620 | && ./build.sh \ 621 | ) \ 622 | || \ 623 | ( \ 624 | test -e rust-empty.mk \ 625 | && $$MAKE -f rust-empty.mk clean \ 626 | && $$MAKE -f rust-empty.mk \ 627 | ) \ 628 | || \ 629 | ( \ 630 | echo "--- Building $$current_git_dir" \ 631 | && $$MAKE clean \ 632 | && $$MAKE \ 633 | ) 634 | done 635 | cd $$current 636 | } 637 | 638 | # Mark main project as visited to avoid infinite loop. 639 | git_dir[i]=$$(pwd) 640 | let i+=1 641 | if [ "$$1" == "deps" ]; then 642 | build_deps 643 | fi 644 | 645 | echo "--- Building $$(pwd)" 646 | ( \ 647 | test -e rust-empty.mk \ 648 | && $$MAKE -f rust-empty.mk clean \ 649 | && $$MAKE -f rust-empty.mk COMPILER_FLAGS+="$$SETTINGS" \ 650 | ) \ 651 | || \ 652 | ( \ 653 | $$MAKE clean 654 | $$MAKE COMPILER_FLAGS+="$$SETTINGS" 655 | ) 656 | 657 | endef 658 | export SYMLINK_BUILD_SCRIPT 659 | 660 | symlink-build: 661 | $(Q)( \ 662 | test -e build.sh \ 663 | && echo "--- The file 'build.sh' already exists" \ 664 | ) \ 665 | || \ 666 | ( \ 667 | echo -e "$$SYMLINK_BUILD_SCRIPT" > build.sh \ 668 | && chmod +x build.sh \ 669 | && echo "--- Created 'build.sh'" \ 670 | && echo "--- Type './build.sh deps' to build everything" \ 671 | ) 672 | 673 | loc: 674 | $(Q)echo "--- Counting lines of .rs files in 'src' (LOC):" \ 675 | && find src/ -type f -name "*.rs" -exec cat {} \; | wc -l 676 | 677 | # Finds the original locations of symlinked libraries and 678 | # prints the commit hash with remote branches containing that commit. 679 | symlink-info: 680 | $(Q) current=$$(pwd) ; \ 681 | for symlib in $$(find $(TARGET_LIB_DIR) -type l) ; do \ 682 | cd $$current ; \ 683 | echo $$symlib ; \ 684 | original_file=$$(readlink $$symlib) ; \ 685 | original_dir=$$(dirname $$original_file) ; \ 686 | cd $$original_dir ; \ 687 | commit=$$(git rev-parse HEAD) ; \ 688 | echo $$commit ; \ 689 | echo "origin:" ; \ 690 | git config --get remote.origin.url ; \ 691 | echo "upstream:" ; \ 692 | git config --get remote.upstream.url ; \ 693 | echo "available in remote branches:" ; \ 694 | git branch -r --contains $$commit ; \ 695 | echo "" ; \ 696 | done \ 697 | > .symlink-info \ 698 | && cd $$current \ 699 | && echo "--- Created '.symlink-info'" \ 700 | && cat .symlink-info 701 | --------------------------------------------------------------------------------