C-C++. Как использовать внедряемые ресурсы приложения, при работе в GCC на Linux

в 16:12, , рубрики: c++, gcc, ld, linked resources

Захотелось как-то мне использовать в Linux внедряемые ресурсы, причём, автоматически. В общем, задача такая:

  1. Имеется Eclipse проект программы на C++.
  2. ОС: Linux Ubuntu. Компилятор: G++
  3. В проекте используются данные из внешних файлов: строки локализации, SQL-запросы, картинки, звуки и т.д.
  4. Все ресурсы необходимо внедрить в исполняемый файл, ибо программу планируется распространять, как портативную.
  5. Кроме того, хочется, что бы процесс был максимально автоматизирован, ибо лень.

Для начала, поиск по форумам дал несколько возможных способов решения задачи. Среди найденных наиболее универсальным мне показалась идея использовать параметр «--format=binary» линковщика «ld». Посты на форумах обещали, что команда вида

g++ -Wl,--format=binary -Wl,my.res -Wl,--format=default

прилинкует к приложению файл «my.res» и создаст два символа — _binary_my_res_start и _binary_my_res_end, указывающих, соответственно, на начало и конец тех самых данных, которые были в прилинкованном файле. Следовательно, обращение к данным из C++ можно было бы осуществить как-то так:

	
	extern const uint8_t my_res_start[]	asm("_binary_my_res_start");
	extern const uint8_t my_res_end[]	asm("_binary_my_res_end");

Но не тут-то было. Пишем всё, как надо, а компилятор недоволен. Символа «_binary_my_res_start», видите ли, он найти не может. Ну ничего, nm нам в помощь. Пишем следующую команду:

nm MyProgramm |grep -w -o -P -e '_binary_[wd_]+'

И получаем:


_binary__home_unknown_workspace_MyProgramm_res_my_res_sql_end
_binary__home_unknown_workspace_MyProgramm_res_my_res_sql_start

Выходит, что имя символа включает в себя весь путь до него, что, в перспективе, может привести к необходимости постоянного переписывания заголовочного файла, содержащего ссылки на ресурсы. Проблема решается, если в событие PostBuild в настройках проекта Eclipse добавить вызов следующего скрипта:

#!/bin/bash
OUTPUT=$1/resources.h
printf 	'#ifndef __RESOURCES_H__n' > "$OUTPUT"
printf 	'#define __RESOURCES_H__nn' >> "$OUTPUT"
printf	'#include <inttypes.h>nn' >> "$OUTPUT"
SYMBOLS=$(nm NewsParser |grep -w -o -P -e '_binary_[wd_]+') >> "$OUTPUT"
VAR_SIZES_LIST=''
for SYMBOL in $SYMBOLS
do
	VAR_NAME=$(echo $SYMBOL | grep -o -P -e 'res_[wd_]+'|cut -c 5-)		

	if [[ -z $(echo $SYMBOL|grep _size) ]] 
	then  
		printf 'textern const uint8_t '$VAR_NAME'[]tasm("'$SYMBOL'");nn' >> "$OUTPUT"
	else 
		START_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'start'
		END_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'end'
		VAR_SIZES_LIST=$VAR_SIZES_LIST$(printf '\tconst uint64_t '$VAR_NAME'\t=\t'$END_VAR' - '$START_VAR';\n\n')
	fi
done
printf "$VAR_SIZES_LIST" >> "$OUTPUT"
printf 	'#endifn' >> "$OUTPUT"
printf  'File '$OUTPUT' is generated.n'
Как добавить скрипт «update_resource.sh», лежащий в корне проекта, в событие PostBuild в настройках проекта Eclipse.

C-C++. Как использовать внедряемые ресурсы приложения, при работе в GCC на Linux - 1

Хорошо. Теперь заголовочный файл будет каждый раз как новенький, а обращаться к данным можно по именам переменных, которые не будут меняться, если только не переименовать сам ресурсный файл. Кроме того, данный скрипт вычисляет размер для каждого ресурса. Не то что бы отнять от указателя на конец указатель на начало было большой проблемой, но, всё же, так удобнее.

Но это, пока что, не всё. Ведь добавление каждого нового ресурса в проект будет превращаться в форменный АД. И эту проблему также можно решить при помощи скрипта, только уже на этапе линковки:


FLAGS=$1
OUTPUT_FLAG=$2
OUTPUT_PREFIX=$3
OUTPUT=$4
INPUTS=$5
RESOURCE_PATH=$6
RESOURCES=''
for res_file in $(ls $RESOURCE_PATH/*)
do
	RESOURCES=$RESOURCES' '-Wl,$res_file
	echo 'Ресурс '$res_file' добавлен в сборку' 
done
g++ $FLAGS $OUTPUT_FLAG $OUTPUT_PREFIX$OUTPUT $INPUTS  -Wl,--format=binary $RESOURCES -Wl,--format=default 
Как в настройках проекта Eclipse заменить вызов стандартного линковщика на собственный скрипт.

C-C++. Как использовать внедряемые ресурсы приложения, при работе в GCC на Linux - 2
  • Красным на картинке выделено место, в котором вместо стандартной команды вызова линковщика прописан путь к скрипту «link.sh», лежащему в корне проекта.
  • Зелёным на картинке выделено место, в котором к обычным параметрам линковщика добавляется ещё один, который сообщает скрипту расположение каталога с ресурсами.
  • Кроме того, важно не забыть обернуть двойными кавычками остальные параметры, чтобы они случайно не побились пробелами не в том порядке, в котором их ждёт скрипт.

Отлично. Теперь все файлы, которые лежат в подкаталоге «res», будут сами попадать в ресурсы при каждой сборке.

Автор: Михайлуц Юрий

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js