diff options
-rw-r--r-- | src/beaver-settings-page.c | 202 | ||||
-rw-r--r-- | src/beaver-settings-page.h | 2 | ||||
-rw-r--r-- | src/beaver-util.c | 203 | ||||
-rw-r--r-- | src/beaver-util.h | 22 | ||||
-rw-r--r-- | src/beaver.h | 3 | ||||
-rw-r--r-- | src/plugin.c | 516 | ||||
-rw-r--r-- | src/plugin.h | 8 |
7 files changed, 750 insertions, 206 deletions
diff --git a/src/beaver-settings-page.c b/src/beaver-settings-page.c index be5f267..0594ec6 100644 --- a/src/beaver-settings-page.c +++ b/src/beaver-settings-page.c @@ -152,6 +152,129 @@ radio_toggled_cb (GtkToggleButton *toggle, gpointer userdata) gtk_toggle_button_get_active (toggle)); } +/* set_property is a must-to-have call back function used by + * Anjuta custom preference registration function for + * gtk combo_box component. + */ +static void +set_property (AnjutaProperty *prop, const gchar *value) +{ + gchar* values; + gchar **vstr; + gint i; + + /* AnjutaProperty data structure are protected and must use + * its own data field fetch utility functions + */ + GtkWidget *combo_box = anjuta_property_get_widget(prop); + + /* combo_box text values are changed/saved during create_ui, + * poky_mode changes, poky_root changes, poky_sdk changes. Its + * text values is saved and associated with the target combo_box + * component, so that the text values could be fetched and + * saved across different functions. + */ + values = g_object_get_data(G_OBJECT(combo_box), OBJ_PROP_TARGET_ARCHS); + + if (!values || strlen(values) <= 0) + return; + + vstr = g_strsplit (values, ",", 0); + if (vstr) + { + for (i=0; vstr[i] != NULL; i++) + { + if (strcmp(value, vstr[i]) == 0) + { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), i); + break; + } + } + g_strfreev(vstr); + } +} + +/* get_property is a must-to-have call back function used by + * Anjuta custom preference registration function for gtk + * combo_box compnent. + */ +static gchar* +get_property(AnjutaProperty *prop) +{ + gchar *values; + gchar **vstr; + gchar *text_value = NULL; + gint idx; + GtkWidget *combo_box = anjuta_property_get_widget(prop); + + values = g_object_get_data(G_OBJECT(combo_box), OBJ_PROP_TARGET_ARCHS); + + if (!values || strlen(values) <= 0) + return text_value; + + vstr = g_strsplit(values, ",", 0); + + if (vstr) + { + idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box)); + if (idx < 0) + idx = 0; + + if (vstr[idx] != NULL) + text_value = g_strdup(vstr[idx]); + + g_strfreev(vstr); + } + + return text_value; +} + +/* Note it's weird that this changed callback function is not implemented + * in anjuta preference framework, and property of preference is internal + * protected and can't be fetched outside anjuta preference code, so + * we have to repeat the implementation of get_property, it seems a little + * strange + */ +static void +target_combo_changed (GtkWidget *widget, gpointer user_data) +{ + AnjutaPreferences *pr; + gchar *text_value = NULL; + gint idx; + gchar *values = NULL; + gchar** vstr; + + pr = ANJUTA_PREFERENCES (g_object_get_data (G_OBJECT (widget), + "AnjutaPreferences")); + values = g_object_get_data(G_OBJECT(widget), OBJ_PROP_TARGET_ARCHS); + if (!values || strlen(values) <= 0) + { + anjuta_preferences_set (pr, PREFS_PROP_TRIPLET, NULL); + return; + } + vstr = g_strsplit(values, ",", 0); + if (vstr) + { + idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); + //if no items selected, select index 0 as default! + if (idx < 0) + idx = 0; + if (vstr[idx] != NULL) + text_value = g_strdup(vstr[idx]); + g_strfreev(vstr); + } + + /* this combo_changed callback will be called before triplet_changed + * call back if the combo box text items or selected index have any + * changes. We need to update the new triplet value, so that + * triplet_changed could get correct new triplet. If the old selected + * text value equals the newly selected text value, then + * triplet_changed need not be called. + */ + anjuta_preferences_set (pr, PREFS_PROP_TRIPLET, text_value); + g_free (text_value); +} + static void create_ui (BeaverSettingsPage *page) { @@ -176,9 +299,14 @@ create_ui (BeaverSettingsPage *page) GtkWidget *qemu_radio; GtkWidget *device_radio; + GtkWidget *target_combo; + gboolean res; - gchar *filename = NULL; + gchar **vstr; + gchar *cur_target = NULL; + gchar *target_combo_value; + int i; gtk_box_set_spacing (GTK_BOX (page), 6); gtk_box_set_homogeneous (GTK_BOX (page), FALSE); @@ -314,27 +442,75 @@ create_ui (BeaverSettingsPage *page) gtk_box_pack_start (GTK_BOX (inner_vbox), inner_alignment, TRUE, FALSE, 0); /* label */ - label = gtk_label_new (_("Toolchain triplet: ")); + label = gtk_label_new (_("Target Architecture: ")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (opts_labels_group, label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - /* entry */ - entry = gtk_entry_new (); - gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); - gtk_size_group_add_widget (opts_fields_group, label); + /* available target architecture is stored in a combo_box */ + target_combo = gtk_combo_box_entry_new_text(); + gtk_box_pack_start (GTK_BOX(hbox), target_combo, TRUE, TRUE, 0); + gtk_size_group_add_widget (opts_fields_group, target_combo); - /* register prop */ - res = anjuta_preferences_register_property_raw (priv->prefs, - entry, + /* Note: we need to save target_combo box widget pointer in preferences + * So event change callback functions outside the UI could get the pointer + * and update the combo box related values */ + g_object_set_data(G_OBJECT(priv->prefs), PREFS_PROP_TARGET_COMBO, + target_combo); + + /* Get target arch values from preferences storage, since it's updated + * by several actions. And also, previous launching values are saved in + * this preference storaget too so that users could save this setting */ + target_combo_value = anjuta_preferences_get(priv->prefs, + PREFS_PROP_TARGET_ARCHS); + + /* Save target arch values and made it associated with combo box, since + * get or set combo box property need to use it.*/ + g_object_set_data(G_OBJECT(target_combo), OBJ_PROP_TARGET_ARCHS, + target_combo_value); + + cur_target = anjuta_preferences_get (priv->prefs, PREFS_PROP_TRIPLET); + + g_debug("CUR arch %s of %s when create_ui", cur_target, target_combo_value); + + /* Fill the combo box values according to the available architectures + * under current UI selections + */ + + if (target_combo_value) + { + vstr = g_strsplit (target_combo_value, ",", 0); + if (vstr) + { + for (i = 0; vstr[i]; i++) { + gtk_combo_box_append_text (GTK_COMBO_BOX(target_combo), vstr[i]); + if (cur_target && strcmp(cur_target, vstr[i]) == 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(target_combo), i); + } + } + g_strfreev (vstr); + } + + res = anjuta_preferences_register_property_custom (priv->prefs, + target_combo, PREFS_PROP_TRIPLET, - NULL, + target_combo_value, 0, - ANJUTA_PROPERTY_OBJECT_TYPE_ENTRY, - ANJUTA_PROPERTY_DATA_TYPE_TEXT); + ANJUTA_PROPERTY_DATA_TYPE_TEXT, + set_property, + get_property); if (!res) - g_warning ("Error adding preference for triplet"); + g_warning ("Error while adding preference for target triplet"); + + /* It's weird that below combo box changed callback registration function + * is missing in custom style registration. We need to add it so that when + * combo box selection or text changed, we can updated related triplet + * in the preference storage + */ + + g_signal_connect(G_OBJECT(target_combo), "changed", + G_CALLBACK(target_combo_changed), NULL); /* Frame for target */ frame = gtk_frame_new (_("<b>Target Options</b>")); diff --git a/src/beaver-settings-page.h b/src/beaver-settings-page.h index 3cced69..b938901 100644 --- a/src/beaver-settings-page.h +++ b/src/beaver-settings-page.h @@ -21,6 +21,7 @@ #include <glib-object.h> #include <gtk/gtk.h> #include <libanjuta/anjuta-shell.h> +#include <libanjuta/anjuta-preferences.h> G_BEGIN_DECLS @@ -60,4 +61,5 @@ GtkWidget *beaver_settings_page_new (AnjutaShell *shell); G_END_DECLS + #endif /* _BEAVER_SETTINGS_PAGE */ diff --git a/src/beaver-util.c b/src/beaver-util.c index 96401b9..0461359 100644 --- a/src/beaver-util.c +++ b/src/beaver-util.c @@ -106,3 +106,206 @@ beaver_util_strv_joinv (gchar **strv_1, ...) return res; } + + +/* Fetch supported target platforms by searching the environment-setup-XXX + * files under poky_sdk_root or poky_tree tmpdir directories. it returns + * a newly allocated char array which is needed to be freed if caller does + * not use it anymore + */ +gchar * +beaver_util_get_archs(gchar *directory) +{ + DIR *dirp; + struct dirent *entry; + gchar *temp = NULL; + gchar *support_arch_string = NULL; + gchar *archname = NULL; + + errno = 0;//clear errno for catching errors + dirp = opendir(directory); + if (errno || !dirp) + { + g_warning("Error while trying to open directory %s: %s!", directory, + g_strerror(errno)); + return NULL; + } +//reset for logging errors + errno = 0; + while(entry = readdir(dirp)) + { + if (errno) + { + g_warning("Error while trying to read directory: %s!", g_strerror(errno)); + goto end; + } + if (entry->d_type != DT_REG) + continue; + archname = entry->d_name; + /* parse the valid environment script files, all valid platform names + * will be connected together with , + */ + if (g_str_has_prefix(archname, ENV_SCRIPT_FILE_PREFIX)) + { + temp = support_arch_string; + if (temp) + support_arch_string = g_strconcat(temp, "," , archname + strlen(ENV_SCRIPT_FILE_PREFIX), NULL); + else + support_arch_string = g_strdup(archname + strlen(ENV_SCRIPT_FILE_PREFIX)); + g_free(temp); + } + + } + g_debug("Get supported arch strings %s", support_arch_string); +end: + closedir(dirp); + return support_arch_string; +} + +/* when target platform selection changes and if we get valid + * corresponding environment script file, we need to update the + * ENV storage hash table. Remember, before the first time + * updating the environment settings, we need to keep the old + * original settings for the conveniences of switching back + * the old environment settings before activating this anjuta poky + * SDK plugin + * Plese Note: This is only a limited parser, if newer script + * shell grammar is used, we should be very careful! + */ +void +update_env_hash(gchar *filename, GHashTable *hash_configs, + GHashTable **old_hash_configs) +{ + FILE *fp; + gchar buf[MAX_CONFIG_LENGTH], temp[MAX_CONFIG_LENGTH], value[MAX_CONFIG_LENGTH]; + gchar *ret_buf, *key; + gint i = 0; + gboolean save = FALSE; + const gchar *env_value; + + + fp = fopen(filename, "r"); + if (!fp) + { + g_warning("Error while opening config file %s!", filename); + return; + } + + /* the old environment settings are saved into one hash table. + * It only needs save once after the poky SDK is activated */ + if (*old_hash_configs) + save = FALSE; + else + { + *old_hash_configs = g_hash_table_new_full + (g_str_hash, g_str_equal, g_free, g_free); + save = TRUE; + } + + while (!feof(fp)) + { + memset(value, 0, MAX_CONFIG_LENGTH); + ret_buf = fgets(buf, MAX_CONFIG_LENGTH, fp); + if (ret_buf) + { + //This is the mark of shell script, skip it for convenience! + //Otherwise, if user modify scripts, it will be wrong + ret_buf = g_strstrip(ret_buf); + if (g_str_has_prefix(ret_buf, "#")) + continue; + if (g_str_has_prefix(ret_buf, "export")) + { + ret_buf += strlen("export"); + ret_buf = g_strstrip(ret_buf); + + i = 0; + while(ret_buf && (*ret_buf != '=')) + temp[i++] = *(ret_buf++); + temp[i] = '\0'; + key = g_strdup(temp); + key = g_strstrip(key); + ret_buf = g_strstrip(ret_buf); + while(ret_buf && (*ret_buf == '=' || *ret_buf == '"' || + *ret_buf == '\'')) + ret_buf++; + //we need very carefully deal with special chars... + //currently we did it manually. Maybe those special + //keys need more processing + i = 0; + while(ret_buf && (*ret_buf != '"') && (*ret_buf != '\n') && + (*ret_buf != '\r') && (*ret_buf != '\'')) + value[i++] = *(ret_buf++); + value[i] = '\0'; + g_debug("NEW ENV key %s, val %s, len %d", key, value, strlen(value)); + /* Please note that hash table key and value must use g_free + * destroyer since it used dynamic allocation mechanism, so + * that we defined g_free in the g_new_hash_table_full create function + */ + g_hash_table_insert(hash_configs, key, g_strstrip(g_strdup(value))); + /* Note: if the old environment variables have never been saved before, + * we will save the old env vars to the hash table for later + * environment variables restore usage + */ + if (save) + { + env_value = g_getenv (key); + g_debug("Save old ENV: key %s value %s", key, env_value); + if (env_value) + g_hash_table_insert(*old_hash_configs, g_strdup(key), g_strdup(env_value)); + } + } + } + } + fclose(fp); +} + +/* update_combo_box need to be called when poky mode + * selection radio changes, sdk_root directory changes + * and poky_root directory changes. It updates the combo + * box text items according to the changed directory + * environment-setupXXX files.*/ + +void +update_combo_box(GtkWidget *p_combo_box, gchar *target_archs) +{ + GtkTreeModel *model; + gint count, i; + gchar **vstr, *active_text; + + if (!p_combo_box) + { + g_warning("Error while fetching combo_box widget!"); + return; + } + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(p_combo_box)); + count = gtk_tree_model_iter_n_children(model, NULL); + + //Firstly remove all old text items in the combo_box + for(i = count - 1; i >= 0; i--) + gtk_combo_box_remove_text(GTK_COMBO_BOX(p_combo_box), i); + + if (!target_archs || strlen(target_archs) <= 0) + { + gtk_widget_set_sensitive(p_combo_box, FALSE); + return; + } + else { + vstr = g_strsplit (target_archs, ",", 0); + + if (vstr) + { + active_text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(p_combo_box)); + for (i = 0; vstr[i]; i++) + { + gtk_combo_box_append_text (GTK_COMBO_BOX(p_combo_box), vstr[i]); + //set back old valid selected text for better user experiences + if (strcmp(vstr[i], active_text) == 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(p_combo_box), i); + } + gtk_widget_set_sensitive(p_combo_box, TRUE); + g_free(active_text); + } + g_strfreev (vstr); + } +} diff --git a/src/beaver-util.h b/src/beaver-util.h index a7c8a61..1cee3d1 100644 --- a/src/beaver-util.h +++ b/src/beaver-util.h @@ -21,22 +21,26 @@ #include <config.h> #include <glib-object.h> +#include <dirent.h> +#include <errno.h> + #include <libanjuta/libanjuta.h> #include <libanjuta/interfaces/ianjuta-message-manager.h> +#define MAX_CONFIG_LENGTH 1000 +#define ENV_SCRIPT_FILE_PREFIX "environment-setup-" + void beaver_util_message_view_buffer_flushed_cb (IAnjutaMessageView *view, gchar *data, gpointer userdata); gchar **beaver_util_strv_concat (gchar **strv_1, gchar **strv_2); gchar **beaver_util_strv_joinv (gchar **strv_1, ...); -#ifdef ANJUTA_CHECK_VERSION -#if LIBANJUTA_MAJOR_VERSION >= 2 && LIBANJUTA_MINOR_VERSION >= 23 -#if LIBANJUTA_MINOR_VERSION >= 28 -#define ANJUTA_2_28_OR_HIGHER -#else -#define ANJUTA_2_23_TO_26 -#endif -#endif -#endif +gchar *beaver_util_get_archs(gchar *directory); +GHashTable *beaver_util_get_configs(gchar *filename); +gchar *get_host_triplet(gchar *env_file); + +void update_env_hash(gchar *filename, GHashTable *hash_configs, + GHashTable **old_hash_configs); +void update_combo_box(GtkWidget *combo_box, gchar *value); #endif /* _BEAVER_UTIL */ diff --git a/src/beaver.h b/src/beaver.h index afa8df9..f5c98af 100644 --- a/src/beaver.h +++ b/src/beaver.h @@ -32,5 +32,8 @@ #define PREFS_PROP_ROOTFS "sdk.rootfs" #define PREFS_PROP_TARGET_IP "sdk.target_ip" #define PREFS_PROP_TARGET_MODE "sdk.target_mode" +#define PREFS_PROP_TARGET_ARCHS "sdk.target_archs" +#define PREFS_PROP_TARGET_COMBO "sdk.target_combo_box" +#define OBJ_PROP_TARGET_ARCHS "available_archs" #endif /* _BEAVER */ diff --git a/src/plugin.c b/src/plugin.c index 3b2e54f..576fa0e 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -45,7 +45,7 @@ #define CONFIGURE_COMMAND "/configure" #define AUTOGEN_COMMAND "/autogen.sh" -#define CROSS_COMMAND "--host=%s" +#define CROSS_COMMAND "CONFIGURE_FLAGS" #define DEPLOY_COMMAND "rsync " \ "-e 'ssh -o \"CheckHostIP no\" " \ @@ -55,11 +55,14 @@ #define LOCAL_GDB_COMMAND "%s-gdb -x %s %s" -#define GDB_SCRIPT "set solib-absolute-prefix %s\n" \ +#define GDB_SCRIPT "set solib-search-path %s\n" \ "target remote %s:2345\n" #define OPROFILEUI_COMMAND "oprofile-viewer -h %s -s %s" +#define SYSROOT "sysroots" +#define TMPDIR "tmp/" + static gpointer anjuta_plugin_sdk_parent_class; /* Callback prototypes needed for actions */ @@ -709,7 +712,8 @@ do_local_gdb (AnjutaPluginSdk *sp) gchar *cmd = NULL; GError *error = NULL; gchar *cur_dir = NULL; - gchar *gdb_prefix = NULL; + gchar *gdb_path = NULL; + const gchar* env_value; /* * create a temporary files and write the client side gdb commands to it, @@ -723,10 +727,18 @@ do_local_gdb (AnjutaPluginSdk *sp) return; } - gdb_prefix = g_build_filename (sp->sdk_root, sp->triplet, NULL); - script_contents = g_strdup_printf (GDB_SCRIPT, gdb_prefix, + //we use search path here according to latest SDK structure changes. + //path is extracted from environment settings PATH! And, ':' should be replaced + //with ';' + env_value = g_getenv ("PATH"); + gdb_path = g_strdup(env_value); + gdb_path = g_strdelimit(gdb_path, ":", ';'); + + script_contents = g_strdup_printf (GDB_SCRIPT, gdb_path, beaver_target_get_ip_address (sp->target)); + g_debug("gdb search path %s\n", script_contents); + channel = g_io_channel_unix_new (fd); status = g_io_channel_write_chars (channel, script_contents, -1, NULL, &error); @@ -747,7 +759,7 @@ do_local_gdb (AnjutaPluginSdk *sp) g_io_channel_unref (channel); g_free (script_contents); - g_free (gdb_prefix); + g_free (gdb_path); terminal = anjuta_shell_get_interface (ANJUTA_PLUGIN (sp)->shell, IAnjutaTerminal, &error); @@ -760,7 +772,7 @@ do_local_gdb (AnjutaPluginSdk *sp) return; } - cmd = g_strdup_printf (LOCAL_GDB_COMMAND, sp->triplet, + cmd = g_strdup_printf (LOCAL_GDB_COMMAND, sp->target_triplet, script_name, sp->gdb_local_path); cur_dir = g_get_current_dir (); child_pid = ianjuta_terminal_execute_command (terminal, cur_dir, cmd, @@ -771,7 +783,7 @@ do_local_gdb (AnjutaPluginSdk *sp) * compatibility */ if (child_pid < 0) - g_warning("Error happned while launching local gdb command\n"); + g_warning("Error happened while launching local gdb command\n"); else if (error != NULL) { g_warning ("Error whilst launching local gdb command: %s", error->message); @@ -865,6 +877,7 @@ action_remote_profile_activate_cb (GtkAction *action, gpointer userdata) AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; gchar *cmd = NULL; gchar *search_path = NULL; + const gchar *env_value; if (!sp->oprofileui_launcher) { @@ -873,7 +886,13 @@ action_remote_profile_activate_cb (GtkAction *action, gpointer userdata) (GCallback)oprofileui_launcher_child_exited_cb, sp); } - search_path = g_build_filename (sp->sdk_root, sp->triplet, NULL); + //we use search path here according to latest SDK structure changes. + //path is extracted from environment settings PATH! And, ':' should be replaced + //with ';' + env_value = g_getenv ("PATH"); + search_path = g_strdup(env_value); + search_path = g_strdelimit(search_path, ":", ';'); + cmd = g_strdup_printf (OPROFILEUI_COMMAND, beaver_target_get_ip_address (sp->target), search_path); @@ -906,7 +925,9 @@ remote_gdb_launcher_child_exited_cb (AnjutaLauncher *launcher, gint child_pid, { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; - if (sp->triplet && sp->sdk_root && + if (sp->target_triplet && + ((sp->poky_mode == POKY_MODE_TOOLCHAIN && sp->sdk_root) || + (sp->poky_mode == POKY_MODE_FULL && sp->poky_root)) && beaver_target_get_state (sp->target) == TARGET_STATE_READY) { gtk_action_set_sensitive (sp->remote_debug_action, TRUE); @@ -947,133 +968,165 @@ message_view_buffer_flushed_cb (IAnjutaMessageView *view, gchar *data, data, "", NULL); } -static gchar * -get_host_component () +/* When new environment variables to be set are stored, + * we need to set each one saved in the hash table. Note + * one special thing: PATH need to be merged with the old + * PATH, otherwise, all useful system paths will be lost! + */ +static void +set_new_env(GHashTable *old_hash_configs, GHashTable *hash_configs) { - struct utsname res; - gchar *os = NULL; - gchar *host_component = NULL; + GHashTableIter iter; + gpointer key, value; + gchar *temp, *oldpath, *newpath; + size_t length; + const gchar *env_value; - uname (&res); - os = g_ascii_strdown (res.sysname, -1); - host_component = g_strdup_printf("%s-%s", res.machine, os); + if (!hash_configs) + { + g_warning("ERROR while set new environment variables," + "to be set variables are NULL!"); + return; + } + if (!old_hash_configs) + { + g_warning("ERROR while set new environment variables," + "old environment hash table can't be NULL"); + return; + } - g_free (os); + g_hash_table_iter_init (&iter, hash_configs); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + /* do something with key and value */ + if (key) + { + /* Do something special for $PATH, we need to + * merge the old path before setting the new path! + */ + if (strcmp(key, "PATH") == 0) + { + temp = g_strrstr((gchar *)value, "$PATH"); + if (!temp) + length = (size_t)(strlen((gchar *)value)); + else + length = (size_t)(strlen((gchar *)value) - strlen(temp)); + oldpath = (gchar *)g_hash_table_lookup(old_hash_configs, key); + if(oldpath && temp) + { + temp = g_strndup((gchar *)value, length); + newpath = g_strconcat(temp, oldpath, NULL);\ + g_setenv((gchar*)key, newpath, 1); + g_debug("SET ENV key %s, value %s", (gchar*)key, (gchar*)newpath); + g_free(newpath); + g_free(temp); + } + else + g_setenv((gchar*)key, (gchar*)value, 1); + } + else + { + g_setenv((gchar*)key, (gchar*)value, 1); + g_debug("SET ENV key %s, value %s", (gchar*)key, (gchar*)value); + } - return host_component; + } + } } -/* Update the path to remove or include our sdk bin directory */ -static void +/* First retrieves new environment variables from the + * configuration files, save it to the hash table and + * then iterate the hash table and set each of the new + * environment variable */ +void update_path (AnjutaPluginSdk *sp) { - const gchar *path; - gchar *new_path_component = NULL; - GArray *path_array = NULL; - char **pathv = NULL; - int i = 0; - gchar *poky_scripts_dir = NULL; - gchar *poky_host_sysroots_bin_dir = NULL; - gchar *poky_host_sysroots_usr_bin_dir = NULL; - gchar *host_component = NULL; - - /* Create new versions of path bits */ - if (sp->poky_mode == POKY_MODE_TOOLCHAIN) - { - if (sp->triplet != NULL && sp->sdk_root != NULL) - new_path_component = g_build_filename (sp->sdk_root, "bin", NULL); - } else { - if (sp->poky_root) - { - host_component = get_host_component (); - poky_scripts_dir = g_build_filename (sp->poky_root, "scripts", NULL); - poky_host_sysroots_usr_bin_dir = g_build_filename (sp->poky_root, "build", "tmp", - "sysroots", host_component, "usr", "bin", NULL); - poky_host_sysroots_bin_dir = g_build_filename (sp->poky_root, "build", "tmp", - "sysroots", host_component, "bin", NULL); - g_free (host_component); - } - } - - /* get current path. do not free */ - path = g_getenv ("PATH"); - - /* split old path up */ - pathv = g_strsplit (path, ":", -1); + gchar *temp, *config_filename; + + temp = g_strconcat(ENV_SCRIPT_FILE_PREFIX, sp->target_triplet, NULL); + + if ((sp->poky_mode == POKY_MODE_TOOLCHAIN) && + sp->target_triplet && sp->sdk_root) + config_filename = g_build_filename (sp->sdk_root,temp, NULL); + else if ((sp->poky_mode == POKY_MODE_FULL) && sp->poky_root) + config_filename = g_build_filename(sp->poky_root, TMPDIR, temp, NULL); + + g_debug("read ENV script, filename %s", config_filename); + update_env_hash(config_filename, sp->env_hash, &sp->old_env_hash); + g_debug("Set new ENV according to script file!"); + set_new_env(sp->old_env_hash, sp->env_hash); + g_free(temp); + g_free(config_filename); +} - /* Convert it into a GArray */ - path_array = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), - g_strv_length (pathv)); - path_array = g_array_insert_vals (path_array, 0, pathv, - g_strv_length (pathv)); +/* Before restore the environment variables to the + * old one before running poky SDK plugins, we need + * to unset some newly set environment variables. + * PATH need not to be unset since it must exist + */ +static void +unset_env(GHashTable *old_hash_configs) +{ + GHashTableIter iter; + gpointer key, value; - /* Remove old versions */ - for (i = 0; i < path_array->len; i++) + if (!old_hash_configs || g_hash_table_size (old_hash_configs) <= 0) { - gchar *tmp = g_array_index (path_array, gchar *, i); - - if ((sp->path_component && g_str_equal (tmp, sp->path_component)) || - (sp->poky_scripts_dir && g_str_equal (tmp, sp->poky_scripts_dir)) || - (sp->poky_host_sysroots_bin_dir - && g_str_equal (tmp, sp->poky_host_sysroots_bin_dir)) || - (sp->poky_host_sysroots_usr_bin_dir - && g_str_equal (tmp, sp->poky_host_sysroots_usr_bin_dir))) - { - path_array = g_array_remove_index (path_array, i); - i--; /* because we've deleted something */ - } + g_warning("Error while trying to unset environment variables" + "old config hash table can't be NULL!"); + return; } - if (sp->poky_mode == POKY_MODE_TOOLCHAIN) + g_hash_table_iter_init (&iter, old_hash_configs); + while (g_hash_table_iter_next (&iter, &key, &value)) { - /* Add the new path component */ - if (new_path_component) - path_array = g_array_prepend_val (path_array, new_path_component); - } else { - if (poky_scripts_dir) - path_array = g_array_prepend_val (path_array, poky_scripts_dir); - - if (poky_host_sysroots_bin_dir) - path_array = g_array_prepend_val (path_array, poky_host_sysroots_bin_dir); - - if (poky_host_sysroots_usr_bin_dir) - path_array = g_array_prepend_val (path_array, poky_host_sysroots_usr_bin_dir); + /* PATH is always needed */ + if (key && (strcmp(key, "PATH") != 0)) + g_unsetenv((gchar*)key); } - - /* Create our new path */ - path = g_strjoinv (":", (gchar **)path_array->data); - setenv ("PATH", path, 1); - - /* Save the components */ - g_free (sp->path_component); - g_free (sp->poky_scripts_dir); - g_free (sp->poky_host_sysroots_bin_dir); - g_free (sp->poky_host_sysroots_usr_bin_dir); - - sp->path_component = new_path_component; - sp->poky_scripts_dir = poky_scripts_dir; - sp->poky_host_sysroots_bin_dir = poky_host_sysroots_bin_dir; - sp->poky_host_sysroots_usr_bin_dir = poky_host_sysroots_usr_bin_dir; - - g_array_free (path_array, TRUE); - g_strfreev (pathv); } -/* - * Add/remove/update the environment to reflect changes to the sdk root or - * triplet +/* Restore old envrionment variable settings before running + * Poky SDK plugin, if anjuta is launching another anjuta + * as its sub process, something might be very tricky, yet + * seems we have no good solution here. For avoid obvious + * mistake, I only restore PATH although all other keys/values + * are actually saved for later use. */ -static void cleanup_environment (AnjutaPluginSdk *sp) { - update_path (sp); - - /* unset environment keys */ - unsetenv ("PKG_CONFIG_SYSROOT_DIR"); - unsetenv ("PKG_CONFIG_PATH"); - unsetenv ("CONFIG_SITE"); + GHashTableIter iter; + gpointer key, value; + + if (!sp->old_env_hash || g_hash_table_size (sp->old_env_hash) <= 0) + { + g_warning("Error while restore ENV: No backup ENV hash created!"); + return; + } + + unset_env(sp->old_env_hash); + + //Note: If anjuta is creating a new project, it is a sub-process. + //we might have problem here since the old environment is set-up + //by its parent anjuta poky plugin. So that we can only set up + //$PATH here for avoid mistakes. Yet I have to say this is not a good + //solution since users might set CC, CXXFLAGS... + g_hash_table_iter_init (&iter, sp->old_env_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + /* Restore old environment variables */ + if (key && (strcmp(key, "PATH") == 0)) + { + g_setenv((gchar*)key, (gchar*)value, 1); + g_debug("Restore ENV key %s, value %s", (gchar*)key, (gchar*)value); + } + } } +/* When get new valid environment variable settings, set new + * env. Otherwise, switch back to the old environment setting + * before running Poky SDK Plugin! + */ static void update_environment (AnjutaPluginSdk *sp) { @@ -1081,55 +1134,16 @@ update_environment (AnjutaPluginSdk *sp) gchar *pkg_config_usr_path = NULL; gchar *pkg_config_path = NULL; - - if (sp->triplet == NULL || - (sp->poky_mode == POKY_MODE_TOOLCHAIN && sp->sdk_root == NULL) || - (sp->poky_mode == POKY_MODE_FULL && sp->poky_root == NULL)) + if (!sp->target_triplet || + (sp->poky_mode == POKY_MODE_TOOLCHAIN && !sp->sdk_root) || + (sp->poky_mode == POKY_MODE_FULL && !sp->poky_root)) { + g_warning("No valid poky environment settings, restore old ENV!"); cleanup_environment (sp); return; } - + cleanup_environment (sp); update_path (sp); - - if (sp->poky_mode == POKY_MODE_TOOLCHAIN) - { - tmp = g_build_filename (sp->sdk_root, sp->triplet, NULL); - setenv ("PKG_CONFIG_SYSROOT_DIR", tmp, 1); - g_free (tmp); - - pkg_config_path = g_build_filename (sp->sdk_root, sp->triplet, "lib", - "pkgconfig", NULL); - pkg_config_usr_path = g_build_filename (sp->sdk_root, sp->triplet, "usr", - "lib", "pkgconfig", NULL); - tmp = g_strdup_printf ("%s:%s", pkg_config_usr_path, pkg_config_path); - - setenv ("PKG_CONFIG_PATH", tmp, 1); - g_free (pkg_config_path); - g_free (pkg_config_usr_path); - g_free (tmp); - - tmp = g_build_filename (sp->sdk_root, "site-config", NULL); - setenv ("CONFIG_SITE", tmp, 1); - g_free (tmp); - } else { - tmp = g_build_filename (sp->poky_root, "build", "tmp", "staging", - sp->triplet, NULL); - setenv ("PKG_CONFIG_SYSROOT_DIR", tmp, 1); - g_free (tmp); - - pkg_config_usr_path = g_build_filename (sp->poky_root, "build", "tmp", - "staging", sp->triplet, "usr", "lib", "pkgconfig", NULL); - pkg_config_path = g_build_filename (sp->poky_root, "build", "tmp", - "staging", sp->triplet, "lib", "pkgconfig", NULL); - tmp = g_strdup_printf ("%s:%s", pkg_config_usr_path, pkg_config_path); - setenv ("PKG_CONFIG_PATH", tmp, 1); - g_free (pkg_config_path); - g_free (pkg_config_usr_path); - g_free (tmp); - - unsetenv ("CONFIG_SITE"); - } } static void @@ -1259,28 +1273,71 @@ setup_target (AnjutaPluginSdk *sp) update_state (sp); } +/* GtkWidget combo_box is weird since we can't clear existing + * text even if the text items below are all removed and the + * active text is actually out-of-date. So sometimes, Anjuta + * preference can't sense the text property changes. We need + * to manually call the triplet text change callback at the + * needed point. + * The main task of this callback is to update plugin's target + * and host triplet + */ +static void +triplet_changed(AnjutaPluginSdk *sp) +{ + + g_free (sp->target_triplet); + sp->target_triplet = NULL; + + sp->target_triplet = anjuta_preferences_get (sp->prefs, PREFS_PROP_TRIPLET); + g_debug("New target triplet %s!", sp->target_triplet); + + if (!sp->target_triplet) + return; + + /* A NULL value set into anjuta perference will be returned as + * empty string */ + if (strlen(sp->target_triplet) <=0) + { + sp->target_triplet = NULL; + return; + } +} + static void sdk_root_preference_notify_cb (AnjutaPreferences *pref, const gchar *key, const gchar *value, gpointer userdata) { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; + GtkWidget *target_combo; g_free (sp->sdk_root); sp->sdk_root = anjuta_preferences_get (sp->prefs, PREFS_PROP_SDK_ROOT); + //update target_combo show items + g_free(sp->target_archs); + sp->target_archs = beaver_util_get_archs(sp->sdk_root); + anjuta_preferences_set(pref, PREFS_PROP_TARGET_ARCHS, sp->target_archs); + + target_combo = (GtkWidget *)g_object_get_data(G_OBJECT(pref), PREFS_PROP_TARGET_COMBO); + g_object_set_data(G_OBJECT(target_combo), OBJ_PROP_TARGET_ARCHS, sp->target_archs); + + update_combo_box(target_combo, sp->target_archs); + triplet_changed(sp); update_environment (sp); + } + + static void triplet_preference_notify_cb (AnjutaPreferences *pref, const gchar *key, const gchar *value, gpointer userdata) { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; - - g_free (sp->triplet); - sp->triplet = anjuta_preferences_get (sp->prefs, PREFS_PROP_TRIPLET); - + triplet_changed(sp); update_environment (sp); + } static void @@ -1318,10 +1375,29 @@ poky_mode_preference_notify_cb (AnjutaPreferences *pref, const gchar *key, const gboolean value, gpointer userdata) { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; + GtkWidget *target_combo; + gchar *dir; sp->poky_mode = anjuta_preferences_get_bool (sp->prefs, PREFS_PROP_POKY_MODE); + g_free(sp->target_archs); + if (sp->poky_mode == POKY_MODE_TOOLCHAIN && sp->sdk_root != NULL) + sp->target_archs = beaver_util_get_archs(sp->sdk_root); + else if (sp->poky_mode == POKY_MODE_FULL && sp->poky_root != NULL) + { + dir = g_build_filename(sp->poky_root, TMPDIR, NULL); + sp->target_archs = beaver_util_get_archs(dir); + g_free(dir); + } + anjuta_preferences_set(pref, PREFS_PROP_TARGET_ARCHS, sp->target_archs); + + target_combo = (GtkWidget *)g_object_get_data(G_OBJECT(pref), PREFS_PROP_TARGET_COMBO); + g_object_set_data(G_OBJECT(target_combo), OBJ_PROP_TARGET_ARCHS, sp->target_archs); + + update_combo_box(target_combo, sp->target_archs); + triplet_changed(sp); update_environment (sp); + } static void @@ -1329,11 +1405,22 @@ poky_root_preference_notify_cb (AnjutaPreferences *pref, const gchar *key, const gchar *value, gpointer userdata) { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)userdata; + GtkWidget *target_combo; g_free (sp->poky_root); sp->poky_root = anjuta_preferences_get (sp->prefs, PREFS_PROP_POKY_ROOT); + g_free(sp->target_archs); + sp->target_archs = beaver_util_get_archs(sp->poky_root); + + anjuta_preferences_set(pref, PREFS_PROP_TARGET_ARCHS, sp->target_archs); + target_combo = (GtkWidget *)g_object_get_data(G_OBJECT(pref), PREFS_PROP_TARGET_COMBO); + g_object_set_data(G_OBJECT(target_combo), OBJ_PROP_TARGET_ARCHS, sp->target_archs); + + update_combo_box(target_combo, sp->target_archs); + triplet_changed(sp); update_environment (sp); + } static void @@ -1437,7 +1524,11 @@ anjuta_plugin_sdk_activate (AnjutaPlugin *plugin) GError *error = NULL; gchar *kernel = NULL; gchar *rootfs = NULL; + gchar *env_filename = NULL; + gchar *temp = NULL; + + g_debug("Activate poky SDK plugin"); ui = anjuta_shell_get_ui (ANJUTA_PLUGIN (plugin)->shell, NULL); sp->action_group = anjuta_ui_add_action_group_entries (ui, "ActionGroupSdk", @@ -1502,7 +1593,8 @@ anjuta_plugin_sdk_activate (AnjutaPlugin *plugin) PREFS_PROP_TARGET_IP, target_ip_preference_notify_cb, sp, NULL); sp->sdk_root = anjuta_preferences_get (sp->prefs, PREFS_PROP_SDK_ROOT); - sp->triplet = anjuta_preferences_get (sp->prefs, PREFS_PROP_TRIPLET); + + sp->target_triplet = anjuta_preferences_get (sp->prefs, PREFS_PROP_TRIPLET); sp->poky_root = anjuta_preferences_get (sp->prefs, PREFS_PROP_POKY_ROOT); @@ -1510,6 +1602,26 @@ anjuta_plugin_sdk_activate (AnjutaPlugin *plugin) sp->target_mode = anjuta_preferences_get_bool (sp->prefs, PREFS_PROP_TARGET_MODE); setup_target (sp); + + /* key and value must be freed when destroy the hash table + * since they are both dynamically allocated when parsing + * config files! + */ + + sp->env_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + if (sp->poky_mode == POKY_MODE_TOOLCHAIN && sp->sdk_root != NULL) + sp->target_archs = beaver_util_get_archs(sp->sdk_root); + else if (sp->poky_mode == POKY_MODE_FULL && sp->poky_root != NULL) + { + temp = g_build_filename(sp->poky_root, TMPDIR, NULL); + sp->target_archs = beaver_util_get_archs(temp); + g_free(temp); + } + + g_debug("When activate, architecture is %s", sp->target_archs); + anjuta_preferences_set(sp->prefs, PREFS_PROP_TARGET_ARCHS, sp->target_archs); + update_environment (sp); sp->project_root_uri_watch = anjuta_plugin_add_watch (plugin, @@ -1534,6 +1646,7 @@ anjuta_plugin_sdk_deactivate (AnjutaPlugin *plugin) GError *error = NULL; IAnjutaMessageManager *msg_manager = NULL; + g_warning("begin deactivate!"); cleanup_environment (sp); ui = anjuta_shell_get_ui (plugin->shell, NULL); @@ -1620,31 +1733,37 @@ anjuta_plugin_sdk_deactivate (AnjutaPlugin *plugin) sp->target = NULL; } + /* * Must set these to NULL because the plugin object might get reused after * deactivation. */ - g_free (sp->sdk_root); - g_free (sp->triplet); + g_free (sp->target_triplet); g_free (sp->poky_root); - + g_hash_table_destroy(sp->env_hash); + g_hash_table_destroy(sp->old_env_hash); g_free (sp->project_root_uri); g_free (sp->path_component); g_free (sp->gdb_local_path); g_free (sp->gdb_remote_command); g_free (sp->remote_command); + g_free(sp->target_archs); sp->sdk_root = NULL; - sp->triplet = NULL; + sp->target_triplet = NULL; sp->poky_root = NULL; + sp->target_archs = NULL; + sp->old_env_hash = NULL; + sp->env_hash = NULL; + + sp->env_hash = NULL; sp->project_root_uri = NULL; sp->path_component = NULL; sp->gdb_local_path = NULL; sp->gdb_remote_command = NULL; sp->remote_command = NULL; - return TRUE; } @@ -1723,37 +1842,68 @@ static gboolean anjuta_environment_override (IAnjutaEnvironment *obj, gchar **dirp, gchar ***argvp, gchar ***envp, GError **err) { AnjutaPluginSdk *sp = (AnjutaPluginSdk *)obj; - gchar **new_argv; + gchar **new_argv, **vstr; + const gchar* configure_flags; + gchar* temp; gsize length = g_strv_length (*argvp); - int i; + gint i, paramcount = 0; if (length < 1) return FALSE; - if (!sp->triplet) // no override for cross command - return TRUE; - + //we only override autogen and configure related params if (g_str_has_suffix(*argvp[0], CONFIGURE_COMMAND) || - g_str_has_suffix(*argvp[0], AUTOGEN_COMMAND)) { - new_argv = g_new (gchar*, length + 2); + g_str_has_suffix(*argvp[0], AUTOGEN_COMMAND)) + { + configure_flags = g_getenv(CROSS_COMMAND); + + if (!configure_flags) + { + g_error("Error while getting configuration flags!"); + return FALSE; + } + + //please make sure that configure flags are splited by + //only a " ". My parser is not very clever! + vstr = g_strsplit(configure_flags, " ", 0); + if (!vstr) + { + g_error("Error while parsing CONFIGURE_FLAGS!"); + return FALSE; + } + + paramcount = g_strv_length (vstr); + + new_argv = g_new (gchar*, length + paramcount + 1); + if (!new_argv) { g_error("Error while allocate args when do environment override!"); + g_strfreev(vstr); return FALSE; } - + /* First copy the command */ new_argv[0] = g_strconcat(*(argvp[0]), NULL); - new_argv[1] = g_strdup_printf (CROSS_COMMAND, sp->triplet); + g_free(*(argvp[0])); - for (i = 0; i < length; i++) { - if (i) - new_argv[1 + i] = g_strconcat(*(*argvp + i), NULL); - g_free(*(*argvp + i)); + /* Insert the newly override parameters */ + for (i = 0; i < paramcount; i++) + { + new_argv[i + 1] = g_strdup (vstr[i]); } + g_strfreev(vstr); + for (i = 1; i < length; i++) + { + /* copy original parameters, the first one need not to be copied! */ + new_argv[paramcount + i] = g_strconcat(*(*argvp + i), NULL); + g_free(*(*argvp + i)); + } /* args must be NULL terminated */ - new_argv[length+1] = NULL; + new_argv[length + paramcount] = NULL; + g_free (*argvp); *argvp = new_argv; + } return TRUE; diff --git a/src/plugin.h b/src/plugin.h index b0a7a25..bdda406 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -72,8 +72,14 @@ struct _AnjutaPluginSdk guint remote_gdb_timeout; - gchar *triplet; + gchar *target_triplet; + gchar *host_triplet; + + GHashTable *env_hash; + GHashTable *old_env_hash; + gchar *sdk_root; + gchar *target_archs; gchar *poky_root; PokyMode poky_mode; |