pw_log_null: pw_log backend that does nothing

pw_log_null implements pw_log backend using an empty inline function.
This ensures that arguments are checked and evaluated consistently with
a functional pw_log backend.

Change-Id: I65761933c50bba228e2154d2bc4f9636bb069b90
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/17280
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index dae1f18..2f52926 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -188,6 +188,7 @@
       "$dir_pw_hdlc_lite:tests",
       "$dir_pw_hex_dump:tests",
       "$dir_pw_log:tests",
+      "$dir_pw_log_null:tests",
       "$dir_pw_log_tokenized:tests",
       "$dir_pw_malloc_freelist:tests",
       "$dir_pw_metric:tests",
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 1296b3f..0d5bdbb 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -85,6 +85,7 @@
     "$dir_pw_kvs:docs",
     "$dir_pw_log:docs",
     "$dir_pw_log_basic:docs",
+    "$dir_pw_log_null:docs",
     "$dir_pw_log_tokenized:docs",
     "$dir_pw_metric:docs",
     "$dir_pw_minimal_cpp_stdlib:docs",
diff --git a/modules.gni b/modules.gni
index 7894054..0ca3cce 100644
--- a/modules.gni
+++ b/modules.gni
@@ -41,6 +41,7 @@
   dir_pw_kvs = get_path_info("pw_kvs", "abspath")
   dir_pw_log = get_path_info("pw_log", "abspath")
   dir_pw_log_basic = get_path_info("pw_log_basic", "abspath")
+  dir_pw_log_null = get_path_info("pw_log_null", "abspath")
   dir_pw_log_tokenized = get_path_info("pw_log_tokenized", "abspath")
   dir_pw_malloc = get_path_info("pw_malloc", "abspath")
   dir_pw_malloc_freelist = get_path_info("pw_malloc_freelist", "abspath")
diff --git a/pw_log_null/BUILD b/pw_log_null/BUILD
new file mode 100644
index 0000000..f87a328
--- /dev/null
+++ b/pw_log_null/BUILD
@@ -0,0 +1,59 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+    "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "headers",
+    hdrs = [
+        "public/pw_log_null/log_null.h",
+        "public_overrides/pw_log_backend/log_backend.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        "//pw_preprocessor",
+    ],
+)
+
+pw_cc_library(
+    name = "pw_log_null",
+    srcs = [
+        "log_null.cc",
+    ],
+    deps = [
+        "//pw_log:facade",
+        "//pw_log_null:headers",
+        "//pw_string",
+        "//pw_sys_io",
+    ],
+)
+
+pw_cc_library(
+    name = "test",
+    srcs = [
+        "test.cc",
+        "test_c.c",
+    ],
+)
diff --git a/pw_log_null/BUILD.gn b/pw_log_null/BUILD.gn
new file mode 100644
index 0000000..e480d0e
--- /dev/null
+++ b/pw_log_null/BUILD.gn
@@ -0,0 +1,49 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+config("config") {
+  include_dirs = [
+    "public",
+    "public_overrides",
+  ]
+  visibility = [ ":*" ]
+}
+
+source_set("pw_log_null") {
+  public_configs = [ ":config" ]
+  public = [ "public_overrides/pw_log_backend/log_backend.h" ]
+  sources = [ "public/pw_log_null/log_null.h" ]
+  friend = [ ":test" ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
+
+pw_test_group("tests") {
+  tests = [ ":test" ]
+}
+
+pw_test("test") {
+  sources = [
+    "test.cc",
+    "test_c.c",
+  ]
+  deps = [ ":pw_log_null" ]
+}
diff --git a/pw_log_null/CMakeLists.txt b/pw_log_null/CMakeLists.txt
new file mode 100644
index 0000000..0d86701
--- /dev/null
+++ b/pw_log_null/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+pw_auto_add_simple_module(pw_log_null
+  IMPLEMENTS_FACADE
+    pw_log
+  PUBLIC_DEPS
+    pw_preprocessor
+)
+
+target_include_directories(pw_log_null PUBLIC public_overrides)
diff --git a/pw_log_null/docs.rst b/pw_log_null/docs.rst
new file mode 100644
index 0000000..6b80809
--- /dev/null
+++ b/pw_log_null/docs.rst
@@ -0,0 +1,23 @@
+.. _chapter-pw-log-null:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+-----------
+pw_log_null
+-----------
+``pw_log_null`` is a ``pw_log backend`` that ignores all ``pw_log`` statements.
+The backend implements ``PW_LOG`` with an empty inline function. Using an empty
+function ensures that the arguments are evaluated and their types are correct.
+Since the function is inline in the header, the compiler will optimize out the
+function call.
+
+This backend can be used to completely disable ``pw_log``, which may be helpful
+in certain development situations (e.g. to avoid circular dependencies).
+
+.. tip::
+  If you are concerned about the resource demands of logging, try tokenizing
+  logs with :ref:`chapter-pw-tokenizer` and :ref:`chapter-pw-log-tokenized`
+  instead of disabling logs completely. Tokenized logs provide exactly same
+  information as plain text logs but use dramatically less resources.
diff --git a/pw_log_null/public/pw_log_null/log_null.h b/pw_log_null/public/pw_log_null/log_null.h
new file mode 100644
index 0000000..0496f1b
--- /dev/null
+++ b/pw_log_null/public/pw_log_null/log_null.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+#pragma once
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_preprocessor/macro_arg_count.h"
+#include "pw_preprocessor/util.h"
+
+PW_EXTERN_C_START
+
+// Empty function for compiling out log statements. Since the function is empty
+// and inline, it should be completely compiled out. This function accomplishes
+// following:
+//
+//   - Uses the arguments to PW_LOG, which avoids "unused variable" warnings.
+//   - Executes expressions passed to PW_LOG, so that the behavior is consistent
+//     between this null backend and a backend that actually logs.
+//   - Checks the printf-style format string arguments to PW_LOG.
+//
+// For compatibility with C and the printf compiler attribute, the declaration
+// and definition must be separate and both marked inline.
+static inline void pw_log_Ignored(int level,
+                                  unsigned int flags,
+                                  const char* module_name,
+                                  const char* message,
+                                  ...) PW_PRINTF_FORMAT(4, 5);
+
+static inline void pw_log_Ignored(int level,
+                                  unsigned int flags,
+                                  const char* module_name,
+                                  const char* message,
+                                  ...) {
+  PW_UNUSED(level);
+  PW_UNUSED(flags);
+  PW_UNUSED(module_name);
+  PW_UNUSED(message);
+}
+
+PW_EXTERN_C_END
+
+#define PW_LOG(level, flags, message, ...) \
+  pw_log_Ignored((level),                  \
+                 (flags),                  \
+                 PW_LOG_MODULE_NAME,       \
+                 message PW_COMMA_ARGS(__VA_ARGS__))
diff --git a/pw_log_null/public_overrides/pw_log_backend/log_backend.h b/pw_log_null/public_overrides/pw_log_backend/log_backend.h
new file mode 100644
index 0000000..3822a51
--- /dev/null
+++ b/pw_log_null/public_overrides/pw_log_backend/log_backend.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+#pragma once
+
+#include "pw_log_null/log_null.h"
diff --git a/pw_log_null/test.cc b/pw_log_null/test.cc
new file mode 100644
index 0000000..6c656be
--- /dev/null
+++ b/pw_log_null/test.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "gtest/gtest.h"
+#include "pw_log_null/log_null.h"
+
+#define PW_LOG_MODULE_NAME "this test!"
+
+extern "C" bool CTest();
+
+namespace {
+
+TEST(LogNull, NoArguments) {
+  PW_LOG(1, 2, "3");
+  PW_LOG(1, 2, "whoa");
+}
+
+TEST(LogNull, WithArguments) {
+  PW_LOG(1, 2, "%s", "hello");
+  PW_LOG(1, 2, "%d + %s == %p", 1, "two", nullptr);
+}
+
+TEST(LogNull, ExpressionsAreEvaluated) {
+  static int global;
+
+  global = 0;
+  bool local = true;
+
+  PW_LOG(1, 2, "You are number%s %d!", (local = false) ? "" : " not", []() {
+    global = 1;
+    return global;
+  }());
+
+  EXPECT_EQ(1, global);
+  EXPECT_FALSE(local);
+}
+
+}  // namespace
diff --git a/pw_log_null/test_c.c b/pw_log_null/test_c.c
new file mode 100644
index 0000000..d68e9fd
--- /dev/null
+++ b/pw_log_null/test_c.c
@@ -0,0 +1,44 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 <stdbool.h>
+#include <stddef.h>
+
+#include "pw_log_null/log_null.h"
+
+#define PW_LOG_MODULE_NAME "c test!"
+
+static int global;
+
+static int IncrementGlobal(void) { return ++global; }
+
+bool CTest() {
+  PW_LOG(1, 2, "3");
+  PW_LOG(1, 2, "whoa");
+  PW_LOG(1, 2, "%s", "hello");
+  PW_LOG(1, 2, "%d + %s == %p", 1, "two", NULL);
+
+  static int global;
+
+  global = 0;
+  bool local = true;
+
+  PW_LOG(1,
+         2,
+         "You are number%s %d!",
+         (local = false) ? "" : " not",
+         IncrementGlobal());
+
+  return global == 1 && !local;
+}