CMake & Clang

Решил изменить сборку с идущего по умолчанию GCC на Clang для CMake проекта. Сходу наткнулся на граблю. Стандартная команда CMake:

set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_COMPILER clang)

Вводит его в вечный цикл, который выглядит как-то так:

You have changed variables that require your cache to be deleted.
Configure will be re-run and you may have to reset some variables.
The following variables have changed:
CMAKE_C_COMPILER= /usr/bin/gcc
CMAKE_CXX_COMPILER= /usr/bin/c++

-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
…и так до бесконечности…

Лечится не удобно, но юзабельно:

export CXX=/usr/bin/clang++
export CC=/usr/bin/clang
cmake CMakeLists.txt
make

А GCC предупреждал…

Есть некий код, приблизительно следующего содержания (упрощенный вариант):

TRACE((ERROR, "Bla-bla-bla. Can't create struct (%terr)", err));

Причем, как мы выяснили с большим трудом, переменная err, это не некий интовый код ошибки, а млять C++ класс! (луч поноса автору этого говна). G++ это дело находит, и, пишет предупреждение:

warning: cannot pass objects of non-POD type ‘struct cERROR’ through ‘...’; call will abort at runtime

Но это предупреждение остается незамеченным среди остальных over 9000 предупрежденией. Но G++ не просто намекает, он еще и генениует вот такой код:

0x01d4fe77 :    mov    DWORD PTR [ebp-0x20],eax
0x01d4fe7a :    jmp    0x1d4fe7e <_ZN8KLParams11DeserializeERP13cSerializablej+668>
0x01d4fe7c :    ud2a  
0x01d4fe7e :    mov    eax,DWORD PTR [ebp-0x20]

Где “ud2a” is the guaranteed illegal opcode on ia32. Пацан сказал – пацан сделал.

Undefined Behavior для C разработчиков

Очень интересная серия статей посвященных Undefined Behavior от Криса Чембера, одного из авторов LLVM.
What Every C Programmer Should Know About Undefined Behavior #1/3
What Every C Programmer Should Know About Undefined Behavior #2/3
What Every C Programmer Should Know About Undefined Behavior #3/3

cURL & Proxy & HTTPS & Authorization == CURLE_RECV_ERROR

Да, баги в тулзах преследуют меня всю неделю. На сей раз отличился cURL с древним багом связанным с запросом отправляемым по HTTPS через прокси. Похоже что ситуация не слишком распространенная, т.к. надо не только работать через прокси, но еще на прокси должна быть включена авторизация и при отправке запроса пароль должен быть не указан. В принципе, в такой ситуации ожидаешь код 407, просишь пользователя ввести пароль и наступает счастье. Но вот cURL думает иначе. Вместо того чтоб вернуть 407 и страничку с ошибкой от прокси, которые судя по его логу присутствуют, зачем-то возвращается CURLE_RECV_ERROR.

Ignore 1380 bytes of response-body
Received HTTP code 407 from proxy after CONNECT
Closing connection #0
Failure when receiving data from the peer

Какого-либо корректного выхода из данной ситуации я не нашел. Так что, при работе через прокси, если пароль не задан и возникает ошибка CURLE_RECV_ERROR, единственное что остается – повторить запрос, предварительно указав параметры авторизации на проксе.

LLVM, чтоб его.

Баг 2008 года в LLVM портит мне жизнь. Берем тестовый файлик следующего содержания:

class RPC_Service
{
public:
  virtual ~RPC_Service ();
};
class EventLoggingInterface
{
  virtual void
    Ev (const char *file, int line, int sev, const char *fmt,
        ...) const = 0;
};
class RPCCore : RPC_Service, EventLoggingInterface
{
  virtual void
    Ev (const char *file, int line, int sev, const char *fmt, ...) const;
};
void
RPCCore::Ev (const char *file, int line, int sev, const char *format, ...) const
{
}

Выполняем команду:

/Developer/usr/bin/llvm-g++ thunk_error.cpp -c

И в результате получаем:

thunk_error.cpp:19: error: generic thunk code fails for method ‘virtual void RPCCore::Ev(const char*, int, int, const char*, ...) const’ which uses ‘...’

Неужели придется отказаться от LLVM? Интерфейс, в моем случае, поменять врятли удастся. Вобщем писать кроссплатформенный код это еще та засада

Грабля в launchd

Неожиданно нарвался на граблю там, где уж совсем не ожидал ее найти, а именно в launchd. Вообще, launchd крайне удобен в управлении демонами, например при помощи него мы при запуске GUI из под пользователя поднимаем рутовый демон. Делается это довольно просто, создается plist и в переменную WatchPath указывается директория, по изменении которой должно запуститься приложение из ProgrammArgument. Сам plist помещается в директорию LaunchAgents для пользовательских приложений и LauchDaemons для системных.
Описанная выше часть работает как часы, и все бы ничего, если бы не возникла необходимость запустить по событию не только демон, но и само GUI. Казалось-бы все просто – добавляем WatchPath со ссылкой на ту же директорию, что и в случае с нашим демоном и вперед. Но вот тут то и зарыта грабля – демон стартует великолепно, а GUI нет. Чисто теоретически, такого происходить не должно, особенно с учетом того, что за директорией LaunchAgents следит launchd запущенный из-под пользователя, а за LauchDaemons рутовый. Но, это только теория, на практике все несколько иначе.
Лечиться созданием отдельной директории, используемой для запуска GUI приложения, которое уже в свою очередь поднимает демон.