Commit 3d335513 authored by Christian Hergert's avatar Christian Hergert

build-manager: add IdeBuildManager and IdeBuildTarget

We need to avoid multiple build operations at once on a build system, so
IdeBuildManager gives us a place to manage that. This allows various
plugins to avoid colliding on builds as well.
parent bf6f3e2e
...@@ -30,9 +30,11 @@ libide_1_0_la_public_headers = \ ...@@ -30,9 +30,11 @@ libide_1_0_la_public_headers = \
buffers/ide-buffer.h \ buffers/ide-buffer.h \
buffers/ide-unsaved-file.h \ buffers/ide-unsaved-file.h \
buffers/ide-unsaved-files.h \ buffers/ide-unsaved-files.h \
buildsystem/ide-build-manager.h \
buildsystem/ide-build-result-addin.h \ buildsystem/ide-build-result-addin.h \
buildsystem/ide-build-result.h \ buildsystem/ide-build-result.h \
buildsystem/ide-build-system.h \ buildsystem/ide-build-system.h \
buildsystem/ide-build-target.h \
buildsystem/ide-builder.h \ buildsystem/ide-builder.h \
buildsystem/ide-configuration-manager.h \ buildsystem/ide-configuration-manager.h \
buildsystem/ide-configuration.h \ buildsystem/ide-configuration.h \
...@@ -87,6 +89,9 @@ libide_1_0_la_public_headers = \ ...@@ -87,6 +89,9 @@ libide_1_0_la_public_headers = \
projects/ide-project-miner.h \ projects/ide-project-miner.h \
projects/ide-project.h \ projects/ide-project.h \
projects/ide-recent-projects.h \ projects/ide-recent-projects.h \
runner/ide-run-manager.h \
runner/ide-runner.h \
runner/ide-runner-addin.h \
runtimes/ide-runtime-manager.h \ runtimes/ide-runtime-manager.h \
runtimes/ide-runtime-provider.h \ runtimes/ide-runtime-provider.h \
runtimes/ide-runtime.h \ runtimes/ide-runtime.h \
...@@ -170,9 +175,11 @@ libide_1_0_la_public_sources = \ ...@@ -170,9 +175,11 @@ libide_1_0_la_public_sources = \
buffers/ide-buffer.c \ buffers/ide-buffer.c \
buffers/ide-unsaved-file.c \ buffers/ide-unsaved-file.c \
buffers/ide-unsaved-files.c \ buffers/ide-unsaved-files.c \
buildsystem/ide-build-manager.c \
buildsystem/ide-build-result-addin.c \ buildsystem/ide-build-result-addin.c \
buildsystem/ide-build-result.c \ buildsystem/ide-build-result.c \
buildsystem/ide-build-system.c \ buildsystem/ide-build-system.c \
buildsystem/ide-build-target.c \
buildsystem/ide-builder.c \ buildsystem/ide-builder.c \
buildsystem/ide-configuration-manager.c \ buildsystem/ide-configuration-manager.c \
buildsystem/ide-configuration.c \ buildsystem/ide-configuration.c \
...@@ -232,6 +239,9 @@ libide_1_0_la_public_sources = \ ...@@ -232,6 +239,9 @@ libide_1_0_la_public_sources = \
projects/ide-project-miner.c \ projects/ide-project-miner.c \
projects/ide-project.c \ projects/ide-project.c \
projects/ide-recent-projects.c \ projects/ide-recent-projects.c \
runner/ide-run-manager.c \
runner/ide-runner.c \
runner/ide-runner-addin.c \
runtimes/ide-runtime-manager.c \ runtimes/ide-runtime-manager.c \
runtimes/ide-runtime-provider.c \ runtimes/ide-runtime-provider.c \
runtimes/ide-runtime.c \ runtimes/ide-runtime.c \
......
...@@ -40,3 +40,9 @@ GListModel to make writing environment editors easier. ...@@ -40,3 +40,9 @@ GListModel to make writing environment editors easier.
## ide-environment-variable.c ## ide-environment-variable.c
A single variable within the environment. A single variable within the environment.
## ide-build-manager.c
The BuildManager provides a convenient place to manage a single build process
for the context. Many build systems do not allow concurrent builds, so this
is a good way to ensure that two plugins do not race to use the build system.
This diff is collapsed.
/* ide-build-manager.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IDE_BUILD_MANAGER_H
#define IDE_BUILD_MANAGER_H
#include <gio/gio.h>
#include "ide-object.h"
#include "buildsystem/ide-builder.h"
G_BEGIN_DECLS
#define IDE_TYPE_BUILD_MANAGER (ide_build_manager_get_type())
G_DECLARE_FINAL_TYPE (IdeBuildManager, ide_build_manager, IDE, BUILD_MANAGER, IdeObject)
gboolean ide_build_manager_get_busy (IdeBuildManager *self);
gchar *ide_build_manager_get_message (IdeBuildManager *self);
GDateTime *ide_build_manager_get_last_build_time (IdeBuildManager *self);
GTimeSpan ide_build_manager_get_running_time (IdeBuildManager *self);
void ide_build_manager_cancel (IdeBuildManager *self);
void ide_build_manager_build_async (IdeBuildManager *self,
IdeBuildTarget *build_target,
IdeBuilderBuildFlags build_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean ide_build_manager_build_finish (IdeBuildManager *self,
GAsyncResult *result,
GError **error);
void ide_build_manager_install_async (IdeBuildManager *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean ide_build_manager_install_finish (IdeBuildManager *self,
GAsyncResult *result,
GError **error);
G_END_DECLS
#endif /* IDE_BUILD_MANAGER_H */
...@@ -128,10 +128,43 @@ ide_build_system_real_get_builder (IdeBuildSystem *self, ...@@ -128,10 +128,43 @@ ide_build_system_real_get_builder (IdeBuildSystem *self,
return NULL; return NULL;
} }
static void
ide_build_system_real_get_build_targets_async (IdeBuildSystem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_assert (IDE_IS_BUILD_SYSTEM (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ide_build_system_real_get_build_targets_async);
g_task_return_pointer (task, g_ptr_array_new (), (GDestroyNotify)g_ptr_array_unref);
}
static GPtrArray *
ide_build_system_real_get_build_targets_finish (IdeBuildSystem *self,
GAsyncResult *result,
GError **error)
{
GTask *task = (GTask *)result;
g_assert (IDE_IS_BUILD_SYSTEM (self));
g_assert (G_IS_TASK (task));
g_assert (g_task_is_valid (task, self));
g_assert (g_task_get_source_tag (task) == ide_build_system_real_get_build_targets_async);
return g_task_propagate_pointer (task, error);
}
static void static void
ide_build_system_default_init (IdeBuildSystemInterface *iface) ide_build_system_default_init (IdeBuildSystemInterface *iface)
{ {
iface->get_builder = ide_build_system_real_get_builder; iface->get_builder = ide_build_system_real_get_builder;
iface->get_build_targets_async = ide_build_system_real_get_build_targets_async;
iface->get_build_targets_finish = ide_build_system_real_get_build_targets_finish;
properties [PROP_PROJECT_FILE] = properties [PROP_PROJECT_FILE] =
g_param_spec_object ("project-file", g_param_spec_object ("project-file",
...@@ -242,3 +275,31 @@ ide_build_system_get_builder (IdeBuildSystem *system, ...@@ -242,3 +275,31 @@ ide_build_system_get_builder (IdeBuildSystem *system,
return IDE_BUILD_SYSTEM_GET_IFACE (system)->get_builder (system, configuration, error); return IDE_BUILD_SYSTEM_GET_IFACE (system)->get_builder (system, configuration, error);
} }
void
ide_build_system_get_build_targets_async (IdeBuildSystem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_async (self, cancellable, callback, user_data);
}
/**
* ide_build_system_get_build_targets_finish:
*
* Returns: (transfer container) (element-type Ide.BuildTarget): An array of build targets
* or %NULL upon failure and @error is set.
*/
GPtrArray *
ide_build_system_get_build_targets_finish (IdeBuildSystem *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_finish (self, result, error);
}
...@@ -33,39 +33,53 @@ struct _IdeBuildSystemInterface ...@@ -33,39 +33,53 @@ struct _IdeBuildSystemInterface
{ {
GTypeInterface parent_iface; GTypeInterface parent_iface;
gint (*get_priority) (IdeBuildSystem *system); gint (*get_priority) (IdeBuildSystem *system);
IdeBuilder *(*get_builder) (IdeBuildSystem *system, IdeBuilder *(*get_builder) (IdeBuildSystem *system,
IdeConfiguration *configuration, IdeConfiguration *configuration,
GError **error); GError **error);
void (*get_build_flags_async) (IdeBuildSystem *self, void (*get_build_flags_async) (IdeBuildSystem *self,
IdeFile *file, IdeFile *file,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
gchar **(*get_build_flags_finish) (IdeBuildSystem *self, gchar **(*get_build_flags_finish) (IdeBuildSystem *self,
GAsyncResult *result, GAsyncResult *result,
GError **error); GError **error);
void (*get_build_targets_async) (IdeBuildSystem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GPtrArray *(*get_build_targets_finish) (IdeBuildSystem *self,
GAsyncResult *result,
GError **error);
}; };
gint ide_build_system_get_priority (IdeBuildSystem *self); gint ide_build_system_get_priority (IdeBuildSystem *self);
void ide_build_system_get_build_flags_async (IdeBuildSystem *self, void ide_build_system_get_build_flags_async (IdeBuildSystem *self,
IdeFile *file, IdeFile *file,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
gchar **ide_build_system_get_build_flags_finish (IdeBuildSystem *self, gchar **ide_build_system_get_build_flags_finish (IdeBuildSystem *self,
GAsyncResult *result, GAsyncResult *result,
GError **error); GError **error);
void ide_build_system_new_async (IdeContext *context, void ide_build_system_new_async (IdeContext *context,
GFile *project_file, GFile *project_file,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
IdeBuildSystem *ide_build_system_new_finish (GAsyncResult *result, IdeBuildSystem *ide_build_system_new_finish (GAsyncResult *result,
GError **error); GError **error);
IdeBuilder *ide_build_system_get_builder (IdeBuildSystem *system, IdeBuilder *ide_build_system_get_builder (IdeBuildSystem *system,
IdeConfiguration *configuration, IdeConfiguration *configuration,
GError **error); GError **error);
void ide_build_system_get_build_targets_async (IdeBuildSystem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GPtrArray *ide_build_system_get_build_targets_finish (IdeBuildSystem *self,
GAsyncResult *result,
GError **error);
G_END_DECLS G_END_DECLS
......
/* ide-build-target.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define G_LOG_DOMAIN "ide-build-target"
#include "ide-build-target.h"
G_DEFINE_INTERFACE (IdeBuildTarget, ide_build_target, IDE_TYPE_OBJECT)
static void
ide_build_target_default_init (IdeBuildTargetInterface *iface)
{
}
/**
* ide_build_target_get_install_directory:
*
* Returns: (nullable) (transfer full): A #GFile or %NULL.
*/
GFile *
ide_build_target_get_install_directory (IdeBuildTarget *self)
{
g_return_val_if_fail (IDE_IS_BUILD_TARGET (self), NULL);
if (IDE_BUILD_TARGET_GET_IFACE (self)->get_install_directory)
return IDE_BUILD_TARGET_GET_IFACE (self)->get_install_directory (self);
return NULL;
}
/* ide-build-target.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IDE_BUILD_TARGET_H
#define IDE_BUILD_TARGET_H
#include <glib-object.h>
#include "ide-object.h"
G_BEGIN_DECLS
#define IDE_TYPE_BUILD_TARGET (ide_build_target_get_type())
G_DECLARE_INTERFACE (IdeBuildTarget, ide_build_target, IDE, BUILD_TARGET, IdeObject)
struct _IdeBuildTargetInterface
{
GTypeInterface parent_iface;
GFile *(*get_install_directory) (IdeBuildTarget *self);
gpointer _reserved1;
gpointer _reserved2;
gpointer _reserved3;
gpointer _reserved4;
gpointer _reserved5;
gpointer _reserved6;
gpointer _reserved7;
gpointer _reserved8;
};
GFile *ide_build_target_get_install_directory (IdeBuildTarget *self);
G_END_DECLS
#endif /* IDE_BUILD_TARGET_H */
...@@ -221,3 +221,42 @@ static void ...@@ -221,3 +221,42 @@ static void
ide_builder_init (IdeBuilder *self) ide_builder_init (IdeBuilder *self)
{ {
} }
void
ide_builder_install_async (IdeBuilder *self,
IdeBuildResult **result,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_BUILDER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
if (result != NULL)
*result = NULL;
IDE_BUILDER_GET_CLASS (self)->install_async (self, result, cancellable, callback, user_data);
}
/**
* ide_builder_install_finish:
*
* Completes an asynchronous call to ide_builder_install_async().
*
* Returns: (transfer none): An #IdeBuildResult.
*/
IdeBuildResult *
ide_builder_install_finish (IdeBuilder *self,
GAsyncResult *result,
GError **error)
{
IdeBuildResult *ret;
g_return_val_if_fail (IDE_IS_BUILDER (self), NULL);
ret = IDE_BUILDER_GET_CLASS (self)->install_finish (self, result, error);
g_return_val_if_fail (!ret || IDE_IS_BUILD_RESULT (ret), NULL);
return ret;
}
...@@ -40,27 +40,43 @@ struct _IdeBuilderClass ...@@ -40,27 +40,43 @@ struct _IdeBuilderClass
{ {
IdeObjectClass parent; IdeObjectClass parent;
void (*build_async) (IdeBuilder *builder, void (*build_async) (IdeBuilder *self,
IdeBuilderBuildFlags flags, IdeBuilderBuildFlags flags,
IdeBuildResult **result, IdeBuildResult **result,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
IdeBuildResult *(*build_finish) (IdeBuilder *builder, IdeBuildResult *(*build_finish) (IdeBuilder *self,
GAsyncResult *result, GAsyncResult *result,
GError **error); GError **error);
void (*install_async) (IdeBuilder *self,
IdeBuildResult **result,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
IdeBuildResult *(*install_finish) (IdeBuilder *self,
GAsyncResult *result,
GError **error);
}; };
IdeConfiguration *ide_builder_get_configuration (IdeBuilder *self); IdeConfiguration *ide_builder_get_configuration (IdeBuilder *self);
void ide_builder_build_async (IdeBuilder *builder, void ide_builder_build_async (IdeBuilder *self,
IdeBuilderBuildFlags flags, IdeBuilderBuildFlags flags,
IdeBuildResult **result, IdeBuildResult **result,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
IdeBuildResult *ide_builder_build_finish (IdeBuilder *builder, IdeBuildResult *ide_builder_build_finish (IdeBuilder *self,
GAsyncResult *result, GAsyncResult *result,
GError **error); GError **error);
void ide_builder_install_async (IdeBuilder *self,
IdeBuildResult **result,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
IdeBuildResult *ide_builder_install_finish (IdeBuilder *self,
GAsyncResult *result,
GError **error);
G_END_DECLS G_END_DECLS
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "buffers/ide-buffer.h" #include "buffers/ide-buffer.h"
#include "buffers/ide-unsaved-file.h" #include "buffers/ide-unsaved-file.h"
#include "buffers/ide-unsaved-files.h" #include "buffers/ide-unsaved-files.h"
#include "buildsystem/ide-build-manager.h"
#include "buildsystem/ide-build-system.h" #include "buildsystem/ide-build-system.h"
#include "buildsystem/ide-configuration-manager.h" #include "buildsystem/ide-configuration-manager.h"
#include "devices/ide-device-manager.h" #include "devices/ide-device-manager.h"
...@@ -59,6 +60,7 @@ struct _IdeContext ...@@ -59,6 +60,7 @@ struct _IdeContext
IdeBackForwardList *back_forward_list; IdeBackForwardList *back_forward_list;
IdeBufferManager *buffer_manager; IdeBufferManager *buffer_manager;
IdeBuildManager *build_manager;
IdeBuildSystem *build_system; IdeBuildSystem *build_system;
IdeConfigurationManager *configuration_manager; IdeConfigurationManager *configuration_manager;
IdeDeviceManager *device_manager; IdeDeviceManager *device_manager;
...@@ -172,6 +174,19 @@ ide_context_get_buffer_manager (IdeContext *self) ...@@ -172,6 +174,19 @@ ide_context_get_buffer_manager (IdeContext *self)
return self->buffer_manager; return self->buffer_manager;
} }
/**
* ide_context_get_build_manager:
*
* Returns: (transfer none): An #IdeBuildManager.
*/
IdeBuildManager *
ide_context_get_build_manager (IdeContext *self)
{
g_return_val_if_fail (IDE_IS_CONTEXT (self), NULL);
return self->build_manager;
}
/** /**
* ide_context_get_build_system: * ide_context_get_build_system:
* *
...@@ -811,6 +826,10 @@ ide_context_init (IdeContext *self) ...@@ -811,6 +826,10 @@ ide_context_init (IdeContext *self)
"context", self, "context", self,
NULL); NULL);
self->build_manager = g_object_new (IDE_TYPE_BUILD_MANAGER,
"context", self,
NULL);
self->device_manager = g_object_new (IDE_TYPE_DEVICE_MANAGER, self->device_manager = g_object_new (IDE_TYPE_DEVICE_MANAGER,
"context", self, "context", self,
NULL); NULL);
......
...@@ -33,6 +33,7 @@ G_DECLARE_FINAL_TYPE (IdeContext, ide_context, IDE, CONTEXT, GObject) ...@@ -33,6 +33,7 @@ G_DECLARE_FINAL_TYPE (IdeContext, ide_context, IDE, CONTEXT, GObject)
IdeBackForwardList *ide_context_get_back_forward_list (IdeContext *self); IdeBackForwardList *ide_context_get_back_forward_list (IdeContext *self);
GFile *ide_context_get_project_file (IdeContext *self); GFile *ide_context_get_project_file (IdeContext *self);
IdeBufferManager *ide_context_get_buffer_manager (IdeContext *self); IdeBufferManager *ide_context_get_buffer_manager (IdeContext *self);
IdeBuildManager *ide_context_get_build_manager (IdeContext *self);
IdeBuildSystem *ide_context_get_build_system (IdeContext *self); IdeBuildSystem *ide_context_get_build_system (IdeContext *self);
IdeConfigurationManager *ide_context_get_configuration_manager (IdeContext *self); IdeConfigurationManager *ide_context_get_configuration_manager (IdeContext *self);
IdeDeviceManager *ide_context_get_device_manager (IdeContext *self); IdeDeviceManager *ide_context_get_device_manager (IdeContext *self);
......
...@@ -83,6 +83,7 @@ typedef struct _IdeProjectFile IdeProjectFile; ...@@ -83,6 +83,7 @@ typedef struct _IdeProjectFile IdeProjectFile;
typedef struct _IdeProjectFiles IdeProjectFiles; typedef struct _IdeProjectFiles IdeProjectFiles;
typedef struct _IdeRunManager IdeRunManager;
typedef struct _IdeRuntime IdeRuntime; typedef struct _IdeRuntime IdeRuntime;
typedef struct _IdeRuntimeManager IdeRuntimeManager; typedef struct _IdeRuntimeManager IdeRuntimeManager;
typedef struct _IdeRuntimeProvider IdeRuntimeProvider; typedef struct _IdeRuntimeProvider IdeRuntimeProvider;
......
...@@ -35,9 +35,11 @@ G_BEGIN_DECLS ...@@ -35,9 +35,11 @@ G_BEGIN_DECLS
#include "buffers/ide-buffer.h" #include "buffers/ide-buffer.h"
#include "buffers/ide-unsaved-file.h" #include "buffers/ide-unsaved-file.h"
#include "buffers/ide-unsaved-files.h" #include "buffers/ide-unsaved-files.h"
#include "buildsystem/ide-build-manager.h"
#include "buildsystem/ide-build-result-addin.h" #include "buildsystem/ide-build-result-addin.h"
#include "buildsystem/ide-build-result.h" #include "buildsystem/ide-build-result.h"
#include "buildsystem/ide-build-system.h" #include "buildsystem/ide-build-system.h"
#include "buildsystem/ide-build-target.h"
#include "buildsystem/ide-builder.h" #include "buildsystem/ide-builder.h"
#include "buildsystem/ide-configuration-manager.h" #include "buildsystem/ide-configuration-manager.h"
#include "buildsystem/ide-configuration.h" #include "buildsystem/ide-configuration.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment