*nix
30 August 2011

Confetti — простая и быстрая конфигурация Вашего проекта

Если Вы пишете проект чуть более среднего, то как правило сталкиваетесь с настройками и конфигурированием. Есть не мало решений на С/С++, хочу рассказать еще про одно довольно-таки простое и красивое решение от Компании mail@Ru, которое я использовал в своем проекте

Сам я пользовался разными парсерами конфига, в последних проектах использовал re2c (конфиг был похож на конфиг nginx). У re2c есть даже немного общего с Конфети — это кодогенерация:
никаких настроечных файлов и структур кодить не надо, все за вас сделает Маг Confetty.

К сожалению, документации ни какой, иначе не было бы этой статьи. Интересующим, милости просим…


Устанавливаем исходники
c GitHub github.com/mailru/confetti
Основное требование — установка YACC.

В папке example есть пример использования. Использование примера и постоение конфига интуитивно понятно. Вся суть заключается в последовательном выполнении нескольких шагов:
// 1. генерация файла конфигурации из шаблона example.cfgtmpl:
../confetti -i example.cfgtmpl -n my_product -f my_product.cfg

// 2. генерация h и с файлов структур конфигурации и их компиляция:
../confetti -i example.cfgtmpl -n my_product -h my_product_cfg.h
../confetti -i example.cfgtmpl -n my_product -c my_product_cfg.c
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c my_product_cfg.c

// 2. генерация и компиляция парсера:
../confetti -i example.cfgtmpl -H prscfg.h
../confetti -i example.cfgtmpl -p prscfg.c
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c prscfg.c

// 4. компиляция и сборка примера
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c example.c
gcc -o example example.o my_product_cfg.o prscfg.o


Если рассмотреть пример, то будет понятно, как внедрять в проект. Далее изложены шаги внедрения в моем проекте.

1. создал папку в источниках своего проекта

cd config.src

2. переписал исполняемый файл

cp confetty /usr/local/bin

3) поправил make файл

применительно к моему проекту:
CONFETTI=/usr/local/bin/confetti
NAME=tarantool_proxy
CFG=tarantool_proxy.cfgtmpl

test_OBJS=tarantool_proxy_cfg.o tnt_config.o prscfg.o

all: $(NAME).cfg test

.SUFFIXES: .o.c

.c.o:
$(CC) $(CFLAGS) $(INCLUDE) -c $<

test: $(test_OBJS)
$(CC) -o $@ $(test_OBJS) $(LIB)

tarantool_proxy: $(test_OBJS)
$(CC) -o $@ $(test_OBJS) $(LIB)

$(NAME).cfg: $(CFG)
$(CONFETTI) -i $< -n $(NAME) -f $(NAME).cfg
$(CONFETTI) -i $< -n $(NAME) -h $(NAME)_cfg.h
$(CONFETTI) -i $< -n $(NAME) -c $(NAME)_cfg.c

prscfg.c: $(CFG)
$(CONFETTI) -i $< -p $@

prscfg.h: $(CFG)
$(CONFETTI) -i $< -H $@

prscfg.c: prscfg.h $(NAME)_cfg.h

$(NAME)_cfg.c: prscfg.h $(NAME)_cfg.h

clean:
rm -f $(NAME).cfg $(NAME)_cfg.c $(NAME)_cfg.h
rm -f prscfg.c prscfg.h
rm -f test
rm *.o

install:
cp $(NAME).def.cfg ../cfg/$(NAME).cfg
cp tarantool_proxy_cfg.o ..
cp prscfg.o ..
cp *.h ..

Обратите внимание, добавил цель install, которая копирует сгенерированные файлы в исходники моего проекта.

3) создал шаблонный файл конфигурации

из example.cfgtmpl
в блоке %{} заменил имя файла на имя, которое должно быть в проекте tarantool_proxy_cfg.h:
%{
#include <prscfg.h>
#include <tarantool_proxy_cfg.h>
 
void out_warning(ConfettyError r, char *format, ...);
%}: 
 

4. создал собственную конфигурацию

и проверил ее:
confetti -i tarantool_proxy.cfgtmpl -n tarantool_proxy -f tarantool_proxy.cfg

Добавил недостающую часть конфига tarantool_proxy.cfgtmpl применительно к своему:
pid = "/usr/local/var/tarantool_proxy.pid"
log = "/usr/local/var/log/tarantool_proxy.log"
 
daemon = 1
 
pool_size = 4096 
 
# count of threads
threads = 4
 
#listen
host = "localhost"
port = 33013
 
# server connections
server = [
 
hostname = "localhost"
port = 33013
namespace = [
  key = NULL, required
]
]
 
namespace = [
type = NULL, required
]
 

5. после генерации файла конфигурации

tarantool_proxy.cfg его переписал в конфигурационный файл по умолчанию: tarantool_proxy.def.cfg и заполнил необходимыми данными (часть из них):

server[0].hostname = "host2"
server[0].port = 33013
 
server[1].hostname = " host1"
server[1].port = 33023
 
 
namespace[1].type = "str"
namespace[0].type = "int"
 
 
server[0].namespace[0].key = "345"
server[0].namespace[1].key = "abc"
 
server[1].namespace[0].key = "xyz"
server[1].namespace[1].key = "345"


Указанные шаблонном файле значения используются по умолчанию.
Далее мною этот файл использовался как дубль, так как файл tarantool_proxy.cfg постоянно переписываются программой confetti

6. создал на базе example.c собственный тестер конфиг файла.


7. делаем make пока все не пройдет

будет удивительно если получится с первого раза :)

Получаем:./test
==========Accepted: 11; Skipped: 0===========
pid => '/usr/local/var/tarantool_proxy.pid'
log => '/usr/local/var/log/tarantool_proxy.log'
daemon => '1'
pool_size => '4096'
threads => '4'
host => 'localhost'
port => '33013'
server[0].hostname => 'localhost'
server[0].port => '33013'
server[0].namespace[0].key => '345'
server[0].namespace[1].key => 'abc'
server[1].hostname => 'tfn24'
server[1].port => '33023'
server[1].namespace[0].key => 'xyz'
server[1].namespace[1].key => '345'
namespace[0].type => 'int'
namespace[1].type => 'str'
==========DIRECT=========
pid=/usr/local/var/tarantool_proxy.pid
daemon=1
keys
==========Destroy=========

Что касается блока «DIRECT» — это тестируется прямой доступ:
printf("==========DIRECT=========\n");
printf("pid=%s\n", cfg.pid);
printf("daemon=%d\n", cfg.daemon);
 

или доступ к элементам массива:
tarantool_proxy_namespace**  it = cfg.namespace;
while( *it != NULL ){
printf("namespace type=%s\n", (*it)->type);
++it;
}
 

Итак, что теперь теперь остается:

  1. Сделать make install, которыйперепишет все необходимые файлы в директорию Вашего проекта.
    Директория config.src — остается тестовым полигоном.
  2. В собственном проекте включить #include {name}_cfg.h где {name} — это то имя, которое Вы выбрали при генерации конфиг файла (в моем проекте tarantool_proxy)
  3. Объявить в исходниках конфигурацию и назначить значения по умолчанию
    tarantool_proxy cfg;
    char *key, *value;
    fill_default_tarantool_proxy(&cfg);
     

  4. Объявить конфигурационный файл и прочитать его:
    int nAccepted, nSkipped;
    FILE *fh = fopen( filename, "r");
     
    if (!fh) {
    fprintf(stderr, "Could not open file %s\n", argv[1]);
    return 1;
    }
     
    useStdout = 1;
    parse_cfg_file_tarantool_proxy(&cfg, fh, 1, &nAccepted, &nSkipped);
    printf("==========Accepted: %d; Skipped: %d===========\n", nAccepted, nSkipped);
    fclose(fh);
     


    Если используем массивы, то объявляем итераторы:
    tarantool_proxy_iterator_t *i;
    = tarantool_proxy_iterator_init();
     
    while ( (key = tarantool_proxy_iterator_next(i, &cfg, &value)) != NULL ) {
    if (value) {
    printf("%s => '%s'\n", key, value);
    free(value);
    } else {
    printf("%s => (null)\n", key);
    }
    }
     



Или пользуемся «прямым» доступом в структуры данных, как это делать упоминалось выше.
Вот и все, переконфигурация проекта (изменение структуры конфига) теперь занимает не более 5 минут.

Надеюсь, что кому-то это съэкономит массу времени.
Спасибо автору Teodor Sigaev

+2
857 4
Comments 4