blob: a2b46db938e9e3c5dd08a1bef850fd0dbf706ec0 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Charmonizer/Core/HeaderChecker.h"
#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/Util.h"
#include <string.h>
#include <stdlib.h>
typedef struct chaz_CHeader {
const char *name;
int exists;
} chaz_CHeader;
/* Keep a sorted, dynamically-sized array of names of all headers we've
* checked for so far.
*/
static struct {
int cache_size;
chaz_CHeader **header_cache;
} chaz_HeadCheck = { 0, NULL };
/* Comparison function to feed to qsort, bsearch, etc.
*/
static int
chaz_HeadCheck_compare_headers(const void *vptr_a, const void *vptr_b);
/* Run a test compilation and return a new chaz_CHeader object encapsulating
* the results.
*/
static chaz_CHeader*
chaz_HeadCheck_discover_header(const char *header_name);
/* Extend the cache, add this chaz_CHeader object to it, and sort.
*/
static void
chaz_HeadCheck_add_to_cache(chaz_CHeader *header);
/* Like add_to_cache, but takes a individual elements instead of a
* chaz_CHeader* and checks if header exists in array first.
*/
static void
chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists);
void
chaz_HeadCheck_init(void) {
chaz_CHeader *null_header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));
/* Create terminating record for the dynamic array of chaz_CHeader
* objects. */
null_header->name = NULL;
null_header->exists = false;
chaz_HeadCheck.header_cache = (chaz_CHeader**)malloc(sizeof(void*));
*(chaz_HeadCheck.header_cache) = null_header;
chaz_HeadCheck.cache_size = 1;
}
int
chaz_HeadCheck_check_header(const char *header_name) {
chaz_CHeader *header;
chaz_CHeader key;
chaz_CHeader *fake = &key;
chaz_CHeader **header_ptr;
/* Fake up a key to feed to bsearch; see if the header's already there. */
key.name = header_name;
key.exists = false;
header_ptr = (chaz_CHeader**)bsearch(&fake, chaz_HeadCheck.header_cache,
chaz_HeadCheck.cache_size,
sizeof(void*),
chaz_HeadCheck_compare_headers);
/* If it's not there, go try a test compile. */
if (header_ptr == NULL) {
header = chaz_HeadCheck_discover_header(header_name);
chaz_HeadCheck_add_to_cache(header);
}
else {
header = *header_ptr;
}
return header->exists;
}
int
chaz_HeadCheck_check_many_headers(const char **header_names) {
static const char test_code[] = "int main() { return 0; }\n";
int success;
int i;
char *code_buf;
size_t needed = sizeof(test_code) + 20;
/* Build the source code string. */
for (i = 0; header_names[i] != NULL; i++) {
needed += strlen(header_names[i]);
needed += sizeof("#include <>\n");
}
code_buf = (char*)malloc(needed);
code_buf[0] = '\0';
for (i = 0; header_names[i] != NULL; i++) {
strcat(code_buf, "#include <");
strcat(code_buf, header_names[i]);
strcat(code_buf, ">\n");
}
strcat(code_buf, test_code);
/* If the code compiles, bulk add all header names to the cache. */
success = chaz_CC_test_compile(code_buf);
if (success) {
for (i = 0; header_names[i] != NULL; i++) {
chaz_HeadCheck_maybe_add_to_cache(header_names[i], true);
}
}
free(code_buf);
return success;
}
int
chaz_HeadCheck_defines_symbol(const char *symbol, const char *includes) {
static const char defines_code[] =
CHAZ_QUOTE( %s )
CHAZ_QUOTE( int main() { )
CHAZ_QUOTE( #ifdef %s )
CHAZ_QUOTE( return 0; )
CHAZ_QUOTE( #else )
CHAZ_QUOTE( return (int)&%s; )
CHAZ_QUOTE( #endif )
CHAZ_QUOTE( } );
long needed = sizeof(defines_code)
+ 2 * strlen(symbol)
+ strlen(includes)
+ 10;
char *buf = (char*)malloc(needed);
int retval;
sprintf(buf, defines_code, includes, symbol, symbol);
retval = chaz_CC_test_compile(buf);
free(buf);
return retval;
}
int
chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
const char *includes) {
static const char contains_code[] =
CHAZ_QUOTE( #include <stddef.h> )
CHAZ_QUOTE( %s )
CHAZ_QUOTE( int main() { return offsetof(%s, %s); } );
long needed = sizeof(contains_code)
+ strlen(struct_name)
+ strlen(member)
+ strlen(includes)
+ 10;
char *buf = (char*)malloc(needed);
int retval;
sprintf(buf, contains_code, includes, struct_name, member);
retval = chaz_CC_test_compile(buf);
free(buf);
return retval;
}
static int
chaz_HeadCheck_compare_headers(const void *vptr_a, const void *vptr_b) {
chaz_CHeader *const *const a = (chaz_CHeader*const*)vptr_a;
chaz_CHeader *const *const b = (chaz_CHeader*const*)vptr_b;
/* (NULL is "greater than" any string.) */
if ((*a)->name == NULL) { return 1; }
else if ((*b)->name == NULL) { return -1; }
else { return strcmp((*a)->name, (*b)->name); }
}
static chaz_CHeader*
chaz_HeadCheck_discover_header(const char *header_name) {
static const char test_code[] = "int main() { return 0; }\n";
chaz_CHeader* header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));
size_t needed = strlen(header_name) + sizeof(test_code) + 50;
char *include_test = (char*)malloc(needed);
/* Assign. */
header->name = chaz_Util_strdup(header_name);
/* See whether code that tries to pull in this header compiles. */
sprintf(include_test, "#include <%s>\n%s", header_name, test_code);
header->exists = chaz_CC_test_compile(include_test);
free(include_test);
return header;
}
static void
chaz_HeadCheck_add_to_cache(chaz_CHeader *header) {
size_t amount;
/* Realloc array -- inefficient, but this isn't a bottleneck. */
amount = ++chaz_HeadCheck.cache_size * sizeof(void*);
chaz_HeadCheck.header_cache
= (chaz_CHeader**)realloc(chaz_HeadCheck.header_cache, amount);
chaz_HeadCheck.header_cache[chaz_HeadCheck.cache_size - 1] = header;
/* Keep the list of headers sorted. */
qsort(chaz_HeadCheck.header_cache, chaz_HeadCheck.cache_size,
sizeof(*(chaz_HeadCheck.header_cache)),
chaz_HeadCheck_compare_headers);
}
static void
chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists) {
chaz_CHeader *header;
chaz_CHeader key;
chaz_CHeader *fake = &key;
/* Fake up a key and bsearch for it. */
key.name = header_name;
key.exists = exists;
header = (chaz_CHeader*)bsearch(&fake, chaz_HeadCheck.header_cache,
chaz_HeadCheck.cache_size, sizeof(void*),
chaz_HeadCheck_compare_headers);
/* We've already done the test compile, so skip that step and add it. */
if (header == NULL) {
header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));
header->name = chaz_Util_strdup(header_name);
header->exists = exists;
chaz_HeadCheck_add_to_cache(header);
}
}