diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ed875d..8a7d30a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -project(gds-render) +project(gds-render LANGUAGES C) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX "/usr/" CACHE PATH "..." FORCE) @@ -77,6 +77,9 @@ add_subdirectory(resources) add_subdirectory(doxygen) add_subdirectory(translations) add_subdirectory(version) +set(FORT_ENABLE_TESTING OFF CACHE INTERNAL "") + +add_subdirectory(3rdparty/libfort) link_directories(${GLIB_LINK_DIRS} ${GTK3_LINK_DIRS} ${CAIRO_LINK_DIRS}) add_definitions(${GLIB2_CFLAGS_OTHER}) @@ -90,5 +93,5 @@ install (TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin ) -target_link_libraries(${PROJECT_NAME} ${GLIB_LDFLAGS} ${GTK3_LDFLAGS} ${CAIRO_LDFLAGS} m version ${CMAKE_DL_LIBS}) +target_link_libraries(${PROJECT_NAME} ${GLIB_LDFLAGS} ${GTK3_LDFLAGS} ${CAIRO_LDFLAGS} m version ${CMAKE_DL_LIBS} fort) diff --git a/command-line.c b/command-line.c index 21e5435..7a40116 100644 --- a/command-line.c +++ b/command-line.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -38,6 +39,41 @@ #include #include #include +#include + +#include + +#ifndef COUNT_OF +#define COUNT_OF(x) (sizeof((x))/sizeof(0[(x)])) +#endif + +enum analysis_format { + ANA_FORMAT_SIMPLE = 0, + ANA_FORMAT_CELLS_ONLY, + ANA_FORMAT_PRETTY, +}; + +struct analysis_format_cmdarg { + enum analysis_format format; + const char *argument; +}; + + + +static const struct analysis_format_cmdarg analysis_format_lookup[] = { + { + .format = ANA_FORMAT_SIMPLE, + .argument = "simple", + }, + { + .format = ANA_FORMAT_PRETTY, + .argument = "pretty", + }, + { + .format = ANA_FORMAT_CELLS_ONLY, + .argument = "cellsonly" + } +}; static int string_array_count(char **string_array) { @@ -136,14 +172,14 @@ static struct gds_cell *find_gds_cell_in_lib(struct gds_library *lib, const char } int command_line_convert_gds(const char *gds_name, - const char *cell_name, - char **renderers, - char **output_file_names, - const char *layer_file, - struct external_renderer_params *ext_param, - gboolean tex_standalone, - gboolean tex_layers, - double scale) + const char *cell_name, + char **renderers, + char **output_file_names, + const char *layer_file, + struct external_renderer_params *ext_param, + gboolean tex_standalone, + gboolean tex_layers, + double scale) { int ret = -1; GList *libs = NULL; @@ -240,4 +276,188 @@ ret_destroy_layer_mapping: return ret; } +static void indent_line(int level) +{ + while (level--) + printf("\t"); +} + +static int printf_indented(int level, const char *format, ...) +{ + int ret; + + va_list a_list; + va_start(a_list, format); + indent_line(level); + ret = vprintf(format, a_list); + va_end(a_list); + + return ret; +} + +static void print_simple_stat(GList *lib_stat_list) +{ + int indentation_level = 0; + GList *lib_iter; + GList *cell_iter; + const struct gds_lib_statistics *lib_stats; + const struct gds_cell_statistics *cell_stats; + + for (lib_iter = lib_stat_list; lib_iter; lib_iter = g_list_next(lib_iter)) { + lib_stats = (const struct gds_lib_statistics *)lib_iter->data; + printf_indented(indentation_level, "Library %s\n", lib_stats->library->name); + indentation_level++; + + for (cell_iter = lib_stats->cell_statistics; cell_iter; cell_iter = g_list_next(cell_iter)) { + cell_stats = (const struct gds_cell_statistics *)cell_iter->data; + printf_indented(indentation_level, "Cell %s\n", cell_stats->cell->name); + indentation_level++; + printf_indented(indentation_level, "Reference count: %zu\n", cell_stats->reference_count); + printf_indented(indentation_level, "Graphics count: %zu\n", cell_stats->gfx_count); + printf_indented(indentation_level, "Vertex count: %zu\n", cell_stats->vertex_count); + printf_indented(indentation_level, "Unresolved children: %d\n", + cell_stats->cell->checks.unresolved_child_count); + printf_indented(indentation_level, "Reference loop: %s\n", + cell_stats->cell->checks.affected_by_reference_loop ? "yes" : "no"); + + indentation_level--; + } + printf_indented(indentation_level, "Cell count: %zu\n", lib_stats->cell_count); + printf_indented(indentation_level, "Reference count: %zu\n", lib_stats->reference_count); + printf_indented(indentation_level, "Graphics count: %zu\n", lib_stats->gfx_count); + printf_indented(indentation_level, "Vertex count: %zu\n", lib_stats->vertex_count); + } + +} + +static void print_table_stat(GList *lib_stat_list) +{ + ft_table_t *table; + GList *lib_stat_iter; + GList *cell_stat_iter; + const struct gds_lib_statistics *lib_stats; + const struct gds_cell_statistics *cell_stats; + + table = ft_create_table(); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + ft_write_ln(table, "Library", "Cell", "GFX", "Vertices", "Refs", "Unresolved Refs", "Loops"); + for (lib_stat_iter = lib_stat_list; lib_stat_iter; lib_stat_iter = g_list_next(lib_stat_iter)) { + lib_stats = (const struct gds_lib_statistics *)lib_stat_iter->data; + ft_printf_ln(table, "%s|%zu|%zu|%zu|%zu|-|-", lib_stats->library->name, lib_stats->cell_count, + lib_stats->gfx_count, lib_stats->vertex_count, lib_stats->reference_count); + for (cell_stat_iter = lib_stats->cell_statistics; cell_stat_iter; + cell_stat_iter = g_list_next(cell_stat_iter)) { + cell_stats = (const struct gds_cell_statistics *)cell_stat_iter->data; + ft_printf_ln(table, "%s|%s|%zu|%zu|%zu|%d|%d", lib_stats->library->name, cell_stats->cell->name, + cell_stats->gfx_count, cell_stats->vertex_count, cell_stats->reference_count, + cell_stats->cell->checks.unresolved_child_count, + cell_stats->cell->checks.affected_by_reference_loop); + } + } + + + printf("%s\n", ft_to_string(table)); + ft_destroy_table(table); + +} + +static void print_statistics(enum analysis_format format, GList *lib_stat_list) +{ + switch (format) { + case ANA_FORMAT_PRETTY: + print_table_stat(lib_stat_list); + break; + default: + print_simple_stat(lib_stat_list); + break; + } +} + +static void print_cell_names(GList *lib_list) +{ + GList *lib_iter; + GList *name_iter; + struct gds_library *lib; + + for (lib_iter = lib_list; lib_iter; lib_iter = g_list_next(lib_iter)) { + lib = (struct gds_library *)lib_iter->data; + for (name_iter = lib->cell_names; name_iter; name_iter = name_iter->next) { + printf("%s\n", (const char *)name_iter->data); + } + } +} + +int command_line_analyze_lib(const char *format, const char *gds_name) +{ + enum analysis_format fmt = ANA_FORMAT_SIMPLE; + size_t idx; + int found = 0; + GList *lib_list = NULL; + const struct gds_library_parsing_opts parsing_opts = { + .simplified_polygons = 0, + }; + int res; + int ret = 0; + GList *lib_iter; + GList *lib_stat_list = NULL; + + g_return_val_if_fail(gds_name, -1002); + + if (format && *format) { + /* Check format if it is not empty */ + for (idx = 0; idx < COUNT_OF(analysis_format_lookup); idx++) { + if (!strcmp(analysis_format_lookup[idx].argument, format)) { + /* Format specifier matches */ + fmt = analysis_format_lookup[idx].format; + found = 1; + } + } + + if (!found) { + fprintf(stderr, "No format matches %s. Using default.\n", format); + } + } + + /* Load the GDS file */ + res = parse_gds_from_file(gds_name, &lib_list, &parsing_opts); + if (res) { + fprintf(stderr, "Error parsing GDS file\n"); + (void)clear_lib_list(&lib_list); + ret = res; + goto return_val; + } + + for (lib_iter = lib_list; lib_iter; lib_iter = g_list_next(lib_iter)) { + res = gds_tree_check_cell_references((struct gds_library *)lib_iter->data); + if (res < 0) { + fprintf(stderr, "Error checking cell references. Will continue...\n"); + } + res = gds_tree_check_reference_loops((struct gds_library *)lib_iter->data); + if (res < 0) { + fprintf(stderr, "Error checking cell reference loops. Will continue...\n"); + } + } + + if (fmt == ANA_FORMAT_CELLS_ONLY) { + print_cell_names(lib_list); + goto return_clear_libs; + } + + + gds_statistics_calc_library(lib_list, &lib_stat_list); + + print_statistics(fmt, lib_stat_list); + + /* Clean up the whole mess */ + + gds_statistics_free_lib_stat_list(&lib_stat_list); +return_clear_libs: + clear_lib_list(&lib_list); +return_val: + return ret; +} + /** @} */ diff --git a/gds-utils/gds-parser.c b/gds-utils/gds-parser.c index 3d960ee..d09307e 100644 --- a/gds-utils/gds-parser.c +++ b/gds-utils/gds-parser.c @@ -50,8 +50,8 @@ */ #define GDS_DEFAULT_UNITS (10E-9) -#define GDS_ERROR(fmt, ...) printf("[PARSE_ERROR] " fmt "\n", ##__VA_ARGS__) /**< @brief Print GDS error*/ -#define GDS_WARN(fmt, ...) printf("[PARSE_WARNING] " fmt "\n", ##__VA_ARGS__) /**< @brief Print GDS warning */ +#define GDS_ERROR(fmt, ...) fprintf(stderr, "[PARSE_ERROR] " fmt "\n", ##__VA_ARGS__) /**< @brief Print GDS error*/ +#define GDS_WARN(fmt, ...) fprintf(stderr, "[PARSE_WARNING] " fmt "\n", ##__VA_ARGS__) /**< @brief Print GDS warning */ #if GDS_PRINT_DEBUG_INFOS /**< @brief standard printf. But can be disabled in code. */ diff --git a/gds-utils/gds-statistics.c b/gds-utils/gds-statistics.c new file mode 100644 index 0000000..c806cfc --- /dev/null +++ b/gds-utils/gds-statistics.c @@ -0,0 +1,137 @@ +/* + * GDSII-Converter + * Copyright (C) 2018 Mario Hüttel + * + * This file is part of GDSII-Converter. + * + * GDSII-Converter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDSII-Converter. If not, see . + */ + +/** + * @file gds-statistics.c + * @brief Statistics of GDS files + * @author Mario Hüttel + * + */ + +#include +#include + +/** + * @addtogroup GDS-Utilities + * @{ + */ + +void gds_statistics_calc_cell(const struct gds_cell *cell, struct gds_cell_statistics *stat) +{ + GList *iter; + GList *vertex_iter; + struct gds_graphics *gfx; + + if (!stat) { + fprintf(stderr, "Internal pointer error.\n"); + return; + } + + stat->cell = NULL; + stat->gfx_count = 0; + stat->reference_count = 0; + stat->vertex_count = 0; + + if (!cell) { + fprintf(stderr, "Internal pointer error.\n"); + return; + + } + + stat->cell = cell; + + /* Sum up references */ + for (iter = cell->child_cells; iter; iter = g_list_next(iter)) + stat->reference_count++; + + /* Sum up graphics objects and vertices */ + for (iter = cell->graphic_objs; iter; iter = g_list_next(iter)) { + gfx = (struct gds_graphics *)iter->data; + stat->gfx_count++; + for (vertex_iter = gfx->vertices; vertex_iter; vertex_iter = g_list_next(vertex_iter)) { + stat->vertex_count++; + } + } +} + +void gds_statistics_calc_library(GList *library_list, GList **lib_stat_list) +{ + + GList *lib_iter; + GList *cell_iter; + struct gds_lib_statistics *lib_stats; + struct gds_cell_statistics *cell_stats; + struct gds_library *gds_lib; + struct gds_cell *cell; + + /* Go through each library/cell and generate statistics */ + for (lib_iter = library_list; lib_iter; lib_iter = g_list_next(lib_iter)) { + gds_lib = (struct gds_library *)lib_iter->data; + lib_stats = (struct gds_lib_statistics *)malloc(sizeof(struct gds_lib_statistics)); + if (!lib_stats) { + g_error("Failed allocating memory"); + } + + lib_stats->library = gds_lib; + lib_stats->gfx_count = 0; + lib_stats->cell_count = 0; + lib_stats->reference_count = 0; + lib_stats->vertex_count = 0; + lib_stats->cell_statistics = NULL; + + for (cell_iter = gds_lib->cells; cell_iter; cell_iter = g_list_next(cell_iter)) { + cell = (struct gds_cell *)cell_iter->data; + cell_stats = (struct gds_cell_statistics *)malloc(sizeof(struct gds_cell_statistics)); + lib_stats->cell_count++; + gds_statistics_calc_cell(cell, cell_stats); + lib_stats->gfx_count += cell_stats->gfx_count; + lib_stats->reference_count += cell_stats->reference_count; + lib_stats->vertex_count += cell_stats->vertex_count; + lib_stats->cell_statistics = g_list_append(lib_stats->cell_statistics, cell_stats); + } + + *lib_stat_list = g_list_append(*lib_stat_list, lib_stats); + } /* for lib */ +} + +static void free_stat_object(gpointer stat_obj) +{ + if (stat_obj) + free(stat_obj); +} + +void gds_statistics_free_lib_stat_list(GList **lib_stat_list) +{ + GList *lib_iter; + struct gds_lib_statistics *lib_stats; + + g_return_if_fail(lib_stat_list); + g_return_if_fail(*lib_stat_list); + + for (lib_iter = *lib_stat_list; lib_iter; lib_iter = g_list_next(lib_iter)) { + lib_stats = (struct gds_lib_statistics *) lib_iter->data; + g_list_free_full(lib_stats->cell_statistics, + free_stat_object); + } + g_list_free_full(*lib_stat_list, free_stat_object); + *lib_stat_list = NULL; +} + + +/** @} */ diff --git a/include/gds-render/command-line.h b/include/gds-render/command-line.h index 242981d..c5d3f9a 100644 --- a/include/gds-render/command-line.h +++ b/include/gds-render/command-line.h @@ -71,6 +71,14 @@ int command_line_convert_gds(const char *gds_name, gboolean tex_layers, double scale); +/** + * @brief Analyze the given GDS file + * @param format Output format of the analysis result + * @param gds_name GDS file name + * @return 0 if successful + */ +int command_line_analyze_lib(const char *format, const char *gds_name); + #endif /* _COMMAND_LINE_H_ */ /** @} */ diff --git a/include/gds-render/gds-utils/gds-statistics.h b/include/gds-render/gds-utils/gds-statistics.h new file mode 100644 index 0000000..9378aa3 --- /dev/null +++ b/include/gds-render/gds-utils/gds-statistics.h @@ -0,0 +1,78 @@ +/* + * GDSII-Converter + * Copyright (C) 2018 Mario Hüttel + * + * This file is part of GDSII-Converter. + * + * GDSII-Converter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDSII-Converter. If not, see . + */ + +/** + * @file gds-parser.h + * @brief Header file for the GDS statistics + * @author Mario Hüttel + */ + +#ifndef _GDS_STATISTICS_H_ +#define _GDS_STATISTICS_H_ + +/** + * @addtogroup GDS-Utilities + * @{ + */ + +#include + +#include + +struct gds_cell_statistics { + size_t gfx_count; + size_t vertex_count; + size_t reference_count; + const struct gds_cell *cell; +}; + +struct gds_lib_statistics { + size_t gfx_count; + size_t vertex_count; + size_t reference_count; + size_t cell_count; + GList *cell_statistics; + const struct gds_library *library; +}; + +/** + * @brief Calculate statistics of a single cell + * @param[in] cell GDS cell + * @param[out] stat Statistics output + */ +void gds_statistics_calc_cell(const struct gds_cell *cell, + struct gds_cell_statistics *stat); + +/** + * @brief Calc statistic information for library + * @param[in] library_list List containing all libraries + * @param[in,out] lib_stat_list Statistic list + */ +void gds_statistics_calc_library(GList * library_list, + GList ** lib_stat_list); + +/** + * @brief Free library statistics GList + * @param[in,out] lib_stat_list List to free + */ +void gds_statistics_free_lib_stat_list(GList ** lib_stat_list); + +/** @} */ + +#endif /* _GDS_STATISTICS_H_ */ diff --git a/main.c b/main.c index e4e76a7..c31a426 100644 --- a/main.c +++ b/main.c @@ -262,6 +262,8 @@ int main(int argc, char **argv) gchar *cellname = NULL; gchar **renderer_args = NULL; gboolean version = FALSE, pdf_standalone = FALSE, pdf_layers = FALSE; + gboolean analyze = FALSE; + gchar *format = NULL; int scale = 1000; int app_status = 0; struct external_renderer_params so_render_params; @@ -275,6 +277,8 @@ int main(int argc, char **argv) GOptionEntry entries[] = { {"version", 'v', 0, G_OPTION_ARG_NONE, &version, _("Print version"), NULL}, + {"analyze", 'A', 0, G_OPTION_ARG_NONE, &analyze, _("Anaylze GDS file"), NULL}, + {"format", 'f', 0, G_OPTION_ARG_STRING, &format, _("Output format of analysis result, Default simple"), "[simple | pretty | cellsonly]"}, {"renderer", 'r', 0, G_OPTION_ARG_STRING_ARRAY, &renderer_args, _("Renderer to use. Can be used multiple times."), "pdf|svg|tikz|ext"}, {"scale", 's', 0, G_OPTION_ARG_INT, &scale, _("Divide output coordinates by "), "" }, @@ -320,10 +324,13 @@ int main(int argc, char **argv) for (i = 2; i < argc; i++) printf(_("Ignored argument: %s"), argv[i]); - app_status = - command_line_convert_gds(gds_name, cellname, renderer_args, output_paths, mappingname, - &so_render_params, pdf_standalone, pdf_layers, scale); - + if (analyze) { + app_status = command_line_analyze_lib(format, gds_name); + } else { + app_status = + command_line_convert_gds(gds_name, cellname, renderer_args, output_paths, mappingname, + &so_render_params, pdf_standalone, pdf_layers, scale); + } } else { app_status = start_gui(argc, argv); }